Cloned, file upload and preview issue with Jquery - javascript

I have a clone creation command. The id parameter created as clone is as follows (example: id = "myid_1"), increments it by one and makes id = "myid_2". So far there is no problem. That way, each object has a unique ID value, but a simple file upload and preview function within these clones causes my function to malfunction.
I created a simple example on jsfiddle. https://jsfiddle.net/magecode/mbk9ps2x/12/
The problem I understand here is that the id value of the file upload in the onhange event must increase in parallel with the image preview id. For this, I resorted to the attr function and tried to increase the id in parallel, but it never changed.
The code I want to do in the example. The value i is always incrementing but is not added to the cloned object.
<div class="clonable-block" data-toggle="cloner">
<a href="#" id="addrow" class="btn btn-info btn-block clonable-button-add" style="border-radius: 0px; font-size: 13px;">
<i class="fa fa-file pr-2"></i>Add Row</a>
<div class="clonable">
<br/>
<br/>
<img id="upload_1" alt="your image" width="100" height="100" class="clonable-increment-id" />
<input type="file" id="preview_1"
onchange="document.getElementById('upload_1').src = window.URL.createObjectURL(this.files[0])">
<br/>
<br/>
</div>
</div>
var i = 1;
$("#addrow").click(function () {
i++;
$("#upload_" + i+"").attr("onchange", "document.getElementById('preview_'+ i + '').src = window.URL.createObjectURL(this.files[0])");
});

I recreated this with vanilla.js, you can then transform it to Jquery.
The thing is that it could be much easier. I recommend you vanilla.js, it has a better overall performance, but i understand the simplicity of Jquery.
Here it goes:
const CloneSource = document.querySelector(".clonable-source");
const buttonRow = document.getElementById("add_row");
const clonesContainer = document.getElementById("clones");
let globalCounter = 1; // Create a globalCounter for the Form IDs
window.addEventListener("change", function(e) {
let target = e.target
// This try catch is just for strictly saying that we
// want to target the .clonable class
try {
let form = target.closest(".clonable")
let fd = new FormData(form)
let img = fd.get("img");
let preview = form.children[0]
let url = URL.createObjectURL(img)
preview.setAttribute("src", url);
} catch {
console.log("No Form")
}
})
buttonRow.addEventListener("click", function(e) {
// Creates Form, img, and input elements
let formSource = document.getElementById("source")
let Form = document.createElement("form")
let Img = document.createElement("img")
let Input = document.createElement("input")
Form.className = `clonable clonable-clone`
Form.setAttribute("id", `clonable-${globalCounter}`) // This add the ID to the Form
globalCounter++; // Sum 1 to the globalCounter;
Img.setAttribute("src", formSource.children[0].getAttribute("src"))
Input.setAttribute("type", "file")
Input.setAttribute("name", "img")
Form.appendChild(Img)
Form.appendChild(Input)
// Then append the form to the clones container
clonesContainer.appendChild(Form);
});
img {
width: 5rem;
}
<button id="add_row">Add Row</button>
<div id="clones">
<form class="clonable clonable-source" id="source">
<img src="#">
<input type="file" name="img">
<p>Select an Image</p>
</form>
</div>

Related

Clicking images to pass the names in a URL string

I have a low level knowledge of javascript and am trying to create a basic image based quiz that passes data back to a search page for local businesses.
Each image would have it's own "tag" as the image ID that relates to one of the options in the search. Ie. Outdoor, Ballroom, Barn, Garden, etc.
Upon submission, it would send the selected image ID's data to www.sitename/search/?_characteristics=TAG1,TAG2,TAG3
That search page will filter the business listings by the tags. Currently it's search function filters the businesses with the following format: website.com/search/?_characteristics=TAG1%2CTAG2
The HTML would look like this:
<img src="http://website.com/image1" id="TAG1"/>
<br/>
<img src="http://website.com/image2" id="TAG2"/>
<form action="https://website.com/business/?&_characteristics=TAG1, TAG2, TAG3" method="get">
<input type="submit" value="View Selected"/>
What you want is the following
Register a click handler on your images to
Capture ids into a collection (array or Set)
Toggle the "selected" class
Register a submit handler on the form to inject an hidden input element, transforming the tag collection into a CSV and setting it to the input value
const form = document.querySelector("form")
const tags = new Set()
document.querySelectorAll("img[id]").forEach(img => {
img.addEventListener("click", () => {
const selected = img.classList.toggle("selected")
tags[selected ? "add" : "delete"](img.id)
})
})
form.addEventListener("submit", (e) => {
const input = Object.assign(document.createElement("input"), {
name: "_characteristics",
type: "hidden",
value: ([...tags]).join(",")
})
form.append(input)
// this is just for the example, omit the following
e.preventDefault()
console.log(`Submitting to ${form.action}?${new URLSearchParams(new FormData(form))}`)
input.remove()
})
img { border: 2px solid grey; }
img.selected { border-color: red; }
<img src="https://picsum.photos/100" id="TAG1"/>
<br/>
<img src="https://picsum.photos/100" id="TAG2"/>
<form action="https://website.com/business/" method="get">
<input type="submit" value="View Selected"/>
</form>
I'm not sure how you want to get the selected img, but here's a way to do it:
Add the class active to the selected img
When clicking on the button, get the id and push it to the output array
Create the link of the tags (id's)
Read the comments below for the detailed explanation.
// Get the images and the submit button
let images = document.querySelectorAll('img');
let btn = document.getElementById('btn');
// Array to hold the tags
let output = [];
// variable to hold the link
let link = '';
// Add the class active to the selected images
for(let i = 0; i < images.length; i++) {
// For each image onclick:
images[i].addEventListener('click', () => {
// Toggle the `active` class on click
images[i].classList.toggle('active');
});
}
// Button onclick:
btn.addEventListener('click', () => {
for(let i = 0; i < images.length; i++) {
// Get the images with the `active` class and push the id to the output array
images[i].classList.contains('active') ? output.push(images[i].getAttribute('id')) : '';
}
// Remove duplicates if found
let uniq = [...new Set(output)];
// Create the link by adding the tags to the string (output values)
link = `www.sitename/search/?_characteristics=${uniq.join(',')}`;
// Print the link to the console
console.log(link);
});
img.active {
box-shadow: 0 0 1px 1px #121212;
}
5. <img src="http://www.gravatar.com/avatar/e1122386990776c6c39a08e9f5fe5648?s=128&d=identicon&r=PG" id="air-conditioned"/>
<br/>
6. <img src="http://www.gravatar.com/avatar/e1122386990776c6c39a08e9f5fe5648?s=128&d=identicon&r=PG" id="outdoor"/>
<br/>
7. <img src="http://www.gravatar.com/avatar/e1122386990776c6c39a08e9f5fe5648?s=128&d=identicon&r=PG" id="indoor"/>
<br/>
8. <img src="http://www.gravatar.com/avatar/e1122386990776c6c39a08e9f5fe5648?s=128&d=identicon&r=PG" id="house"/>
<br/>
<button id="btn">Submit</button>

JavaScript : onchange function causes error/ didn't work

The Following code will add File upload and preview field.
<b>This single img works but not in js</b> <br>
<img id="img" alt="your image" width="100" height="100" />
<input type="file" onchange="document.getElementById('img').src = window.URL.createObjectURL(this.files[0])">
<br/>
No of Img <input type="text" id="noi" name="noi" value="" onkeyup="addFields()">
<br />
<script>
function addFields(){
// Number of inputs to create
var number = document.getElementById("noi").value;
// Container <div> where dynamic content will be placed
var container = document.getElementById("container");
var array = ["CRICTICAL","HIGH","LOW","INFO"];
// Clear previous contents of the container
while (container.hasChildNodes()) {
container.removeChild(container.lastChild);
}
for (i=1;i<=number;i++){
var img = document.createElement("img");
img.width="100";
img.height="100";
img.id="img "+i;
var upload = document.createElement("input");
upload.type="file";
//This part is Not Working_______________
upload.onchange="document.getElementById(img.id).src = window.URL.createObjectURL(this.files[0])";
//________________________________________
container.appendChild(img);
container.appendChild(upload);
container.appendChild(document.createElement("br"));
}
}
</script>
<div id="container"/>
Problem :
Without the Container appendChild function the code works fine, you can see it in the first three lines of code.
You have to run a function inside upload.onchange. Something like this:
upload.onchange= function () {
document.getElementById(img.id).src = window.URL.createObjectURL(this.files[0])
}
Other way to do:
upload.addEventListener('change', function () {
document.getElementById(img.id).src =
window.URL.createObjectURL(this.files[0])
})
Problem Solved
Working Fine.
All image fields can able to preform upload and preview function of 'n' fields.
<b>This single img works but not in js</b> <br>
<img id="img" alt="your image" width="100" height="100" />
<input type="file" onchange="document.getElementById('img').src = window.URL.createObjectURL(this.files[0])">
<br/>
No of Img <input type="text" id="noi" name="noi" value="" onkeyup="addFields()">
<br />
<script>
function addFields(){
// Number of inputs to create
var number = document.getElementById("noi").value;
// Container <div> where dynamic content will be placed
var container = document.getElementById("container");
var array = ["CRICTICAL","HIGH","LOW","INFO"];
// Clear previous contents of the container
while (container.hasChildNodes()) {
container.removeChild(container.lastChild);
}
for (i=1;i<=number;i++){
var img = document.createElement("img");
img.width="100";
img.height="100";
img.id="img "+i;
var upload = document.createElement("input");
upload.type="file";
upload.id="upload "+i;
//Working_______________
upload.onchange=upload.onchange= function () {
var img_id=this.getAttribute('id');
var imgId = img_id.charAt(7) ;
document.getElementById("img "+imgId).src = window.URL.createObjectURL(this.files[0])
}
//________________________________________
container.appendChild(img);
container.appendChild(upload);
container.appendChild(document.createElement("br"));
}
}
</script>
<div id="container"/>

How to add working dropify inputs dynamically

I have form which gets clone when user click on add more button .
This is how my html looks:
<div class="col-xs-12 duplicateable-content">
<div class="item-block">
<button class="btn btn-danger btn-float btn-remove">
<i class="ti-close"></i>
</button>
<input type="file" id="drop" class="dropify" data-default-file="https://cdn.example.com/front2/assets/img/logo-default.png" name="sch_logo">
</div>
<button class="btn btn-primary btn-duplicator">Add experience</button>
...
</div>
This my jquery part :
$(function(){
$(".btn-duplicator").on("click", function(a) {
a.preventDefault();
var b = $(this).parent().siblings(".duplicateable-content"),
c = $("<div>").append(b.clone(true, true)).html();
$(c).insertBefore(b);
var d = b.prev(".duplicateable-content");
d.fadeIn(600).removeClass("duplicateable-content")
})
});
Now I want every time user clicks on add more button the id and class of the input type file should be changed into an unique, some may be thinking why I'm doing this, it I because dropify plugin doesn't work after being cloned, but when I gave it unique id and class it started working, here is what I've tried :
function randomString(len, an){
an = an&&an.toLowerCase();
var str="", i=0, min=an=="a"?10:0, max=an=="n"?10:62;
for(;i++<len;){
var r = Math.random()*(max-min)+min <<0;
str += String.fromCharCode(r+=r>9?r<36?55:61:48);
}
return str;
} var ptr = randomString(10, "a");
var className = $('#drop').attr('class');
var cd = $("#drop").removeClass(className).addClass(ptr);
Now after this here is how I initiate the plugin $('.' + ptr).dropify().
But because id is still same I'm not able to produce clone more than one.
How can I change the id and class everytime user click on it? is there a better way?
Working Fiddle.
Problem :
You're cloning a div that contain already initialized dropify input and that what create the conflict when you're trying to clone it and reinitilize it after clone for the second time.
Solution: Create a model div for the dropify div you want to clone without adding dropify class to prevent $('.dropify').dropify() from initialize the input then add class dropify during the clone.
Model div code :
<div class='hidden'>
<div class="col-xs-12 duplicateable-content model">
<div class="item-block">
<button class="btn btn-danger btn-float btn-remove">
X
</button>
<input type="file" data-default-file="http://www.misterbilingue.com/assets/uploads/fileserver/Company%20Register/game_logo_default_fix.png" name="sch_logo">
</div>
<button class="btn btn-primary btn-duplicator">Add experience</button>
</div>
</div>
JS code :
$('.dropify').dropify();
$("body").on("click",".btn-duplicator", clone_model);
$("body").on("click",".btn-remove", remove);
//Functions
function clone_model() {
var b = $(this).parent(".duplicateable-content"),
c = $(".model").clone(true, true);
c.removeClass('model');
c.find('input').addClass('dropify');
$(b).before(c);
$('.dropify').dropify();
}
function remove() {
$(this).closest('.duplicateable-content').remove();
}
Hope this helps.
Try this:
$(function() {
$(document).on("click", ".btn-duplicator", function(a) {
a.preventDefault();
var b = $(this).parent(".duplicateable-content"),
c = b.clone(true, true);
c.find(".dropify").removeClass('dropify').addClass('cropify')
.attr('id', b.find('[type="file"]')[0].id + $(".btn-duplicator").index(this)) //<here
$(c).insertBefore(b);
var d = b.prev(".duplicateable-content");
d.fadeIn(600).removeClass("duplicateable-content")
})
});
Fiddle
This does what you specified with an example different from yours:
<div id="template"><span>...</span></div>
<script>
function appendrow () {
html = $('#template').html();
var $last = $('.copy').last();
var lastId;
if($last.length > 0) {
lastId = parseInt($('.copy').last().prop('id').substr(3));
} else {
lastId = -1;
}
$copy = $(html);
$copy.prop('id', 'row' + (lastId + 1));
$copy.addClass('copy');
if(lastId < 0)
$copy.insertAfter('#template');
else
$copy.insertAfter("#row" + lastId);
}
appendrow();
appendrow();
appendrow();
</script>
Try adding one class to all dropify inputs (e.g. 'dropify'). Then you can set each elements ID to a genereted value using this:
inputToAdd.attr('id', 'dropify-input-' + $('.dropify').length );
Each time you add another button, $('.dropify').length will increase by 1 so you and up having a unique ID for every button.

Remove checked checkboxes

I am making a To-do list, where I want to be able to add new tasks, and delete tasks that are checked off. However, it seems my function just deletes all tasks, not just the ones that are checked off. Neither does it seem to allow new tasks to be added.
html:
<h1 id="title"> To-do list</h1>
<div id="task_area">
</div>
<input type="text" id="putin"></input>
<button id="add">add</button>
javascript:
<button id="clear">Clear completed tasks</button>
var tasks = document.getElementById("task_area")
var new_task = document.getElementById("add")
var clear = document.getElementById("clear")
new_task.addEventListener("click", function() {
var putin = document.getElementById("putin")
var input = document.createElement('input')
input.type = "checkbox"
var label = document.createElement('label')
label.appendChild(document.createTextNode(putin.value))
task_area.appendChild(input)
task_area.appendChild(label)
})
clear.addEventListener("click", function() {
for (i = 0; i < task_area.children.length; i++) {
if (task_area.children[i].checked === true) {
task_area.remove(tasks.children[i])
}
}
})
jsfiddle: https://jsfiddle.net/4coxL3um/
.remove removes the element you are calling it from, and doesn't take an argument for what to remove. The following:
task_area.remove(tasks.children[i])
should be
tasks.children[i].remove()
EDIT: As Mononess commented below, this will only remove the checkboxes and not the labels. While you could delete both using Jayesh Goyani's answer below, it's probably better that each input/label pair be wrapped in a single div or span for easier management.
You could try adding an event listener to each child of task_area that calls the below function. Haven't gotten a chance to test it out, and may not fulfill all of your requirements, but should get the job done.
function removeClicked() {
this.parentNode.removeChild(this);
}
Please try with the below code snippet. Below code will help you to remove selected checkbox with label.
<body>
<h1 id="title">To-do list</h1>
<div id="task_area">
</div>
<input type="text" id="putin" />
<button id="add">add</button>
<button id="clear">Clear completed tasks</button>
<script>
var tasks = document.getElementById("task_area")
var new_task = document.getElementById("add")
var clear = document.getElementById("clear")
new_task.addEventListener("click", function () {
var putin = document.getElementById("putin")
var input = document.createElement('input')
input.type = "checkbox"
var label = document.createElement('label')
label.appendChild(document.createTextNode(putin.value))
task_area.appendChild(input)
task_area.appendChild(label)
//document.getElementById("task_area").innerHTML = putin.value
})
clear.addEventListener("click", function () {
for (i = 0; i < task_area.children.length; i++) {
if (task_area.children[i].checked === true) {
tasks.children[i].nextSibling.remove();
tasks.children[i].remove();
}
}
})
</script>
</body>
Please let me know if any concern.

Accessing a childNode or sibling attributes after click event fired on button in JavaScript NOT JQuery

I'm having a problem in understanding how to get attributes in this situation. Imagine I have an UL of images and buttons in this markup. Note that the idea would be for these items to be dynamically generated from a mysql database.
<div id="div-items">
<ul>
<li name="add" data-price="20"><figure><img src="images/Desert.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary">Add Item</button></figcaption></figure></li>
<li name="add" data-price="10"><figure><img src="images/Penguins.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary">Add Item</button></figcaption></figure></li>
<li name="add" data-price="15"><figure><img src="images/Jellyfish.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary">Add Item</button></figcaption></figure></li>
</ul>
I am setting an eventListener to each button for each li item as so:
var addItems = document.getElementsByTag('button');
addItemsLen = addItems.length;
var mycount = 0;
for (var i=0; i < addItemsLen; i++)
{
var thisitem = addItems[i];
thisitem.addEventListener("click", getPrice, false);
};
So, when a button click event is fired I want to be able to access the details of the li items. For instance I would like to be able to get the img src or the data-price. The getPrice function is this
function getPrice(e)
{
//I know this is the button but I want the li ???
var el = e.target;
var price = el.getAttribute("data-price");
alert(price);
}
But that does not work. I can see that the target is the button what I want is the li but is this a childNode or sibling of the button. I have tried a couple of things but can't work out what is needed in getPrice to get the data-price in the li...
NOTE: For all those JQuery gurus - Yes I know it can be done in JQuery...Please don't suggest JQuery I want a pure JavaScript solution
Hopefully someone can set me right here.
To solve this in pure JavaScript, you can try the following. You already have the button, so you need to find the nearest ancestor 'li' element:
var findParent = function(el, nodeName) {
while (el !== null) {
if (el.nodeName.toLowerCase() == nodeName) {
return el;
}
el = el.parentNode;
}
return null;
};
Then, rewrite the getPrice function to find the ancestor li:
var getPrice = function(event) {
var button = event.target;
var li = findParent(button, 'li');
var price = li.getAttribute('data-price');
alert(price);
};
EDIT: from here, if you need to access the image, you can use various techniques. If you are not concerned about compatibility with old IE (see http://caniuse.com/queryselector for more information), you can use querySelector to find the child image:
var getPrice = function(event) {
var button = event.target;
var li = findParent(button, 'li');
var price = li.getAttribute('data-price');
alert(price);
var image = li.querySelector('img');
var imageSource = img.getAttribute('src');
alert(imageSource);
};
It's kind of hokey but this will work...
function getPrice(e)
{
var el = e.target;
var price = el.parentNode.parentNode.parentNode.getAttribute("data-price");
console.log(price);
}
In this case e.target is the button which is nested a few layers deep in the <li> tag. Since you're generating this list from the server, my personal preference would be to, change your HTML to be...
<div id="div-items">
<ul>
<li name="add" data-price="20"><figure><img src="images/Desert.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary" data-price="20">Add Item</button></figcaption></figure></li>
<li name="add" data-price="10"><figure><img src="images/Penguins.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary" data-price="10">Add Item</button></figcaption></figure></li>
<li name="add" data-price="15"><figure><img src="images/Jellyfish.jpg" width="50%" height="50%"><figcaption><button class="btn btn-primary" data-price="50">Add Item</button></figcaption></figure></li>
</ul>
If you do that you can keep your javascript the way you have it.

Categories

Resources