I've been messing around with this for a couple of hours now, but for the life of me, cannot seem to get it to work, so any help greatly appreciated.
For context, I have a page which has a bootstrap form contained within it, the form contains divs which contain the form elements. There are 5 divs and the page by default, but I have a button, that when clicked I want to duplicate and continue to add as many additional "lines" to the form as the user would like.
This is the code I have in the button itself, within the bootstrap html doc:
<button style="margin-top:30px" id="button" onclick="duplicate()">ADD NEW LINE</button>
And this is the javascript that I have:
document.getElementById('button').onclick = duplicate;
var i = 0;
var original = document.getElementById('duplicater');
function duplicate() {
var clone = original.cloneNode(true); // "deep" clone
clone.id = "duplicater" + ++i; // there can only be one element with an ID
original.parentNode.appendChild(clone);
}
The Javascript file is physically located (locally at present) at /js/addnewline.js
And I have it referenced at the bottom of the HTML page like this:
<script src="/js/addnewline.js"></script>
When loading up the page and clicking the button, nothing happens.
Any pointers are welcomed.
Thank you in advance,
Gavin
you have a wrong id in you button. you have id="button" instead of id="duplicater"
this is your solution:
<button style="margin-top:30px" id="duplicater" onclick="duplicate()">ADD NEW LINE</button>
The problem is in this line " document.getElementById('button').onclick = duplicate;" here you have defined the click event but at the same time you have used onclick inside the button remove this " document.getElementById('button').onclick = duplicate;" line and the id of the button should be duplicater
<button style="margin-top:30px" id="duplicater" onclick="duplicate()">ADD NEW LINE</button>
var i = 0;
var original = document.getElementById('duplicater');
console.log(original)
function duplicate() {
console.log(original)
var clone = original.cloneNode(true); // "deep" clone
clone.id = "duplicater" + ++i; // there can only be one element with an ID
original.parentNode.appendChild(clone);
}
Related
This question already has answers here:
Is it possible to append to innerHTML without destroying descendants' event listeners?
(13 answers)
Closed 6 years ago.
I have a function for adding buttons to a page.
var count = 0;
function createOnclickFunction(number)
{
return function()
{
alert("This is button number " + number);
}
}
function addButton(data)
{
var newbutton = "..." //creates string from data
var element = document.getElementById("mydiv");
var children = element.children;
element.innerHTML = newbutton + element.innerHTML;
var currentCount = count;
children[0].onclick = createOnclickFunction(currentCount)
count++;
}
It basically creates a button into the html based off some data.
Every time I add a button, I would like it to be added to the start of div #mydiv, and since newbutton is also not an Element, but a String, I have to modify the innerHtml to add it to the start of #mydiv.
Afterwards, I must get the element (to add an onclick), by getting the first child of #mydiv.
However, after adding a second button to my page, the first button onclick no longer works.
If I modify my code to only add one button, everything works fine.
So now, only the top button (the latest added button), can be clicked.
How can I fix this?
I've also tried to use element.firstChild instead of element.children[0].
Thanks in advance everyone!
EDIT:
Here is a jsfiddle: ( as you can see the only button that works is stackoverflow )
https://jsfiddle.net/7c7gtL26/
It seems you misunderstood the problem. The problem is that you are overwriting innerHTML in order to insert contents.
Never use innerHTML to insert contents. It will remove all the internal data, like event listeners. This is what happens here.
If you want to prepend some HTML string, use insertAdjacentHTML:
var count = 0;
function createOnclickFunction(number) {
return function() {
alert("This is button number " + number);
}
}
function addButton(data) {
var newbutton = "<button>Click me</button>" //creates string from data
var element = document.getElementById("mydiv");
element.insertAdjacentHTML('afterbegin', newbutton);
var children = element.children;
children[0].onclick = createOnclickFunction(count)
count++;
}
addButton();
addButton();
addButton();
<div id="mydiv"></div>
It's very difficult for me to show you my code, as it's all over the place, but what I'm trying to do is this:
I am injecting html code into the DOM in a function buy using .innerHTML, I wish to add a click event to an icon that is being injected in this step, as at this moment in time I know its id. So after I've injected it I write:
document.getElementById(product.id+"x").addEventListener("click", removeItem);
product.id is created above and this element is a 'X' button, that when clicked will be removed from the screen.
The trouble is, this code is run many times as there are many items to be displayed on the screen. And when finished, only the last even made fires when the 'X' button is pressed.
Any suggestions?
EDIT:
I am unable to use jquery in this project.
Here is my code:
function createHTML(targetID, product) {
var target = document.getElementById(targetID);
total = (parseFloat(total) + parseFloat(product.price)).toFixed(2);;
target.innerHTML += '<article class="item" id="'+product.id+'"><img class="item_img" src="../'+product.image+'" width=100 height=100><h1 class="item_name">'+product.name+'</h1><p class="item_description">'+product.desc+'</p><h1 class="item_quantity">Quantity: '+product.quantity+'</h1><h1 class="item_price">£'+product.price+'</h1><i id="'+product.id+'x" class="fa fa-times"></i></article>';
document.getElementById(product.id+"x").addEventListener("click", removeItem, true);
}
So you're adding new elements to a container by overwriting the innerHTML or appending to it using +=. This is your problem. When you overwrite the innerHTML or append to it, you are destroying and recreating all elements within it and this causes them to lose any bound event handlers (ie your click handler).
This fiddle reproduces your problem. Only the last button has a click handler.
The solution is to build DOM elements using document.createElement() and use appendChild() or similar to append them, instead of creating/appending raw HTML. This way, your previous elements event handlers will remain intact.
This Fiddle uses DOM nodes instead of raw HTML and all buttons have a click handler.
Example fix:
var container = document.getElementById("container");
var elem;
function clicky(){
alert("clicked");
}
for(var i=0; i<4; i++){
elem = document.createElement('button');
elem.id = "btn_" + i;
elem.appendChild(document.createTextNode('Click'));
elem.addEventListener("click", clicky);
container.appendChild(elem);
}
I quess you do something like that
//Place where you add elements.
var container = document.body;
you create element and add listener to that element(button):
var button = '<button id="btn1x">Button 1</button>';
container.innerHTML += button;
//product.id = 'btn1';
document.getElementById(product.id+"x").addEventListener("click", removeItem);
and then you add in the same way new elements and add for them event listeners before next element will be generated.
If my quess is right, then your problem is that you replace whole content of container so previous event listens are lost.
stringVariable += 'abc' is the same as stringVariable = stringVariable + 'abc'. Because of that you overwrite html.
You should create elements from functions, not from string as you do now.
var button = document.createElement('button');
button.id = product.id + 'x';
button.innerText = 'Button 1'; // Title of button.
//Add button to container.
container.appendChild(button);
//Add event listener to created button.
button.addEventListener('click', myFunc);
UPDATE:
There are a way to parse your string to element.
First create container where will be set inner html from string, then get from that temp container first element (or more elements, depends from your html string), then add them to container and add to these elements listeners.
DEMO: http://jsfiddle.net/3cD4G/1/
HTML:
<div id="container">
</div>
Javascript:
var container = document.getElementById("container");
function clicky(){
alert("clicked");
}
var tempContainer = document.createElement('div');
for(var i=0; i<4; i++){
//Create your element as string.
var strElem = "<button type='button' id='btn_" + i + "'>Click</button>";
//Add that string to temp container (his html will be replaced, not added).
tempContainer.innerHTML = strElem.trim();//Trim function used to prevent empty textnodes before element.
//Get element from temp container.
var button = tempContainer.children[0];
//Empty tempContainer for better security (But about which security I'm talking in JavaScript in string element generation :) )
tempContainer.innerHTML = '';
//Add your button to container.
container.appendChild(button);
//Add event listener to button:
//document.getElementById("btn_" + i).onclick = clicky;
//Better way to add event listener:
button.addEventListener('click', clicky);
}
DEMO: http://jsfiddle.net/3cD4G/1/
I created multiple divs which class="extra". Then I add delete buttons to each div in order to remove each div respectively. So my code is:
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick= function(i){ return function(){ exr[i].parentNode.removeChild(exr[i]) } }(i);
}
The problem is, the button can't removethe button it should remove. It seems that after last delete, the index is changed. How to avoid this from happening?
Thanks!
Use this within the onclick function to reference the button that was clicked — so by replacing excr[i] with this.parentNode, your code should execute as intended.
Do this...
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<inserter.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.index = i;
delbt.onclick= function(i){ var me = this; return function(){ exr[me.index].parentNode.removeChild(exr[i]) } }(i);
}
Since getElementsByClassName() returns a live set, you could make a shallow copy first:
var exr = [].slice.call(document.getElementsByClassName("extra"), 0);
You could use currentTarget on the click event.
This will only remove the parent div of the button clicked.
var exr = document.getElementsByClassName("extra");
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick= function( event ){
event.currentTarget.parentElement.remove();
}
}
The code looks like it should work, but only as long as you don't add or remove elements. And that's what you are doing.
There is a simpler and more reliable solution: You can use this inside the event handler to refer to the current element.
delbt.onclick = function() {
var div = this.parentNode; // you want to remove the div, not the button
div.parentNode.removeChild(div);
};
I recommend to read the excellent articles about event handling on quirksmode.org, especially about traditional event handling (since that's what you are using).
getElementsByClassName() returns an HTMLCollection. This is a live list that changes when the underlying structure changes. So even if you don't activly delete the elements from the list, they still will be removed from it automatically when you remove the corresponding HTML Element.
By passing i to the anonymous function, you kind of fixed the index. So whenever you click on the third delete button it will try to delete the third element in the list. But if you have already deleted the first and the second element, the list will be modified too. So your third button will be represented by the first item in your list but it will try to find the third one.
To avoid this problem you can pass the complete element to the anonymous function and not just the index:
for(var i = 0 ;i<exr.length;i++){
var delbt = document.createElement("button");
delbt.className="floatbutton_3 font_b"
delbt.innerHTML="delete";
exr[i].appendChild(delbt);
delbt.onclick = function(el){ return function(){ el.parentNode.removeChild(el) } }(exr[i]);
}
I'll add a jQuery solution:
$('.extra').append(function() {
return $('<button />', {'class':'floatbutton_3 font_b', html:'delete'});
});
$('.floatbutton_3').on('click', function() {
$(this).closest('div').remove();
});
FIDDLE
What I would like to do is to create a div by clicking on a button. In that div there will be another button if clicked will delete the div that it is in. So potentially the first button will create many div's with this delete button inside but I want the delete button to only delete the div that it is within. Any suggestions?
If you do not need to keep the "delete button" on your page, bind the click event on each of them and use .removeChild on the parent element of the parent div.
FIDDLE DEMO
You can do something like:
<script>
var addDiv = (function() {
var div = document.createElement('div');
div.appendChild(document.createTextNode('some text'));
var button = document.createElement('button');
button.appendChild(document.createTextNode('Remove...'));
button.type = "button";
return function() {
var d = div.cloneNode(true);
var b = button.cloneNode(true);
b.onclick = function() {
var d = this.parentNode;
d.parentNode.removeChild(d);
};
d.appendChild(b);
document.body.appendChild(d);
};
}())
</script>
<button onclick="addDiv()">Add a div</button>
The above is just a trivial example to demonstrate one way to go about it. Note that if you clone an element, listeners added as properties or by addEventListener are dropped (this script would be very much simpler if they weren't).
HTML:
<input type="button" id='create' value="Create div!"/>
JS:
var i = 0;
document.onclick = function(e) {
var t = e.target;
if(t.id == 'create'){
t.parentNode.innerHTML += '<div>I AM A CHILD '+(++i)+' <input type="button" class="child" value="DELETE ME!"/><br/></div>';
}
if (t.className == 'child') {
t.parentNode.outerHTML = '';
}
};
using document.onclick and detecting the target element can be used as a live click to monitor newly created divs.
Its early and my brain may be a little foggy and the code may be a little dirty
Here is a jsfiddle
I need to create a new child to an existing element. The question add onclick event to newly added element in javascript helped me a lot.
I just can not define it as the first child. I can place it using position, but this is still insufficient. I searched on sites about JavaScript, but I found nothing.
Here is my code:
if( !document.getElementById('callbackButton')){
callback = function(){
var button = document.createElement('button');
button.id= 'callbackButton';
var textbutton =document.createTextNode("Liste des années d'étude");
button.appendChild(textbutton );
button.style.position='absolute';
button.style.top="60px";
button.style.left="45px";
button.style.width="200px";
button.style.height="18px";
button.onclick = function(){
getElementsByIdStarWith('etageres-virtuelle')[0].innerHTML = oldInnerHtml;
document.getElementById('etageres-virtuelles-etudes-germaniques').innerHTML = oldInnerHtml;
wrapPager();
};
document.getElementById('etageres-virtuelles-etudes-germaniques').appendChild(button);
};
This code works very well.
But this code doesn't work:
document.getElementById('etageres-virtuelles-etudes-germaniques').firstChild.nodeValue = button;
document.getElementById('etageres-virtuelles-etudes-germaniques').firstChild.nodeData = button;
This is not what I want. I want to display this new element on first place.
Any help would be appreciated. Thank.
Try this (untested):
var yourEl = document.getElementById('etageres-virtuelles-etudes-germaniques');
yourEl.insertBefore(button, yourEl.firstChild);