HTML:
<input type="button" value="Start" id="start">
JavaScript:
var inp = document.querySelectorAll('.z1');
Array.prototype.forEach.call(inp, function(item, i, arr) {
item[i].addEventListener('click', function() {
alert('fsdfs');
}, false);
});
document.getElementById('start').addEventListener('click',function() {
var gg = '<input type="button" value="Button1" class="z1"><input type="button" value="Button2" class="z1">';
document.body.insertAdjacentHTML('beforeEnd', gg);
});
The idea is that when you click on the inserted in the body INPUT should trigger alert.
5 Answers 5
Alert won't fire for new elements because you never bind events to them. Instead delegate click event to the body, where it will bubble eventually:
document.body.addEventListener('click', function(e) {
if (e.target.className == 'z1') {
alert('fsdfs');
}
}, false);
document.getElementById('start').addEventListener('click',function() {
var gg = '<input type="button" value="Button1" class="z1"><input type="button" value="Button2" class="z1">';
document.body.insertAdjacentHTML('beforeEnd', gg);
});
<input type="button" value="Start" id="start">
Just remember in case of delegated events you need to check that event occurred on the right element. Like in the demo you can check event target class name.
3 Comments
a element with an image inside it will cause the click not to alert. Recursively walking up the DOM tree is needed then. Still the cleanest solution!This updates your original code with a working version, it's even generic. Just pass selector string and it append the click function.
function setClickHandlers(selector)
{
var inp = document.querySelectorAll(selector);
Array.prototype.forEach.call(inp, function(item, i, arr) {
item.addEventListener('click', clickHandlerButton
, false);
});
}
function clickHandlerButton()
{
alert('fsdfs');
}
document.getElementById('start').addEventListener('click',function() {
var gg = '<input type="button" value="Button1" class="z1"><input type="button" value="Button2" class="z1">';
document.body.insertAdjacentHTML('beforeEnd', gg);
setClickHandlers('.z1');
});
To prevent your program from adding anonymous functions to each button you can reference a function in the click event. This saves some memory space and makes updates to the code easier.
However dfsq has the most elegant solution, this requires only one click handler to be set. This works beautifully when elements have no children.
4 Comments
} before the , false..z1 items on each click ?Your selector will select nothing from the HTML you have shown, however, if your selector was working, then this is the correct code
Array.prototype.forEach.call(inp, function(item, i, arr) {
item.addEventListener('click', function() {
alert('fsdfs');
}, false);
});
Comments
Not directly an answer to this problem, but a (better) solution to the underlaying implementation.
I recommend to use this alternative function:
// forEach method, could be shipped as part of an Object Literal/Module
var forEach = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) {
callback.call(scope, i, array[i]); // passes back stuff we need
}
};
// Usage:
// optionally change the scope as final parameter too, like ECMA5
var myNodeList = document.querySelectorAll('li');
forEach(myNodeList, function (index, value) {
console.log(index, value); // passes index + value back!
});
There's a very good and detailed blog post (by a Googler) here, expaining the [].forEach.call(NodeList)-hack and why you should avoid using it.
EDIT: I know that an answer should preferable contain the relevant parts of any external references. However, I couldn't say it any better *and* it's a github.io link, which shouldn't vanish soon.
1 Comment
.querySelectorAll() on elements that don't exist yet. (Also, not all of the reasons in that blog post are good reasons. E.g., #2 is just wrong in that it ignores the fact that you don't have to use an anonymous function.)For completeness, here's a (削除) complete (削除ここまで) different approach:
// Objects could hold additional attributes to be set in addBtns();
// see http://fiddle.jshell.net/eyecatchup/e39wwzwu/2/show/light/ for details.
var btnMap = [{val: 'Button1'}, {val: 'Button2'}],
startBtn = document.getElementById('start');
function addBtns() {
for (i in btnMap) {
var _el = startBtn.cloneNode(!0, !1);
_el.removeAttribute('id'); // remove id property of source node!
_el.value = btnMap[i].val;
_el.setAttribute('onclick', "alert('fsdfs')");
_el.className = 'z1'; // optional!
startBtn && startBtn.parentNode.appendChild(_el);
}
}
(function(){
startBtn && startBtn.addEventListener('click', addBtns);
})();
*{margin:0;padding:10px;} .z1{margin-left:10px; font-weight:bold;}
<input type="button" value="Start" id="start">
Array.prototype.forEach.callis executed in this code. They get added by clicking while the call is fired before this click. There are simply0elements matching.z1item[i]- see my solution belowitem[i]is wrong asitemwould already be the dom element. Soitem[i]would be trying to access a property on the dom element.