2

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.

asked Jan 2, 2015 at 14:28
5
  • 1
    What does happen? Nothing at all? An error in the browser console? Some other behaviour? Commented Jan 2, 2015 at 14:29
  • jsfiddle.net/t5xz93tr Alert is not displayed. Commented Jan 2, 2015 at 14:30
  • 1
    The elements aren't there when Array.prototype.forEach.call is executed in this code. They get added by clicking while the call is fired before this click. There are simply 0 elements matching .z1 Commented Jan 2, 2015 at 14:31
  • The problem is in the item[i] - see my solution below Commented Jan 2, 2015 at 14:35
  • 2
    item[i] is wrong as item would already be the dom element. So item[i] would be trying to access a property on the dom element. Commented Jan 2, 2015 at 14:35

5 Answers 5

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.

answered Jan 2, 2015 at 14:32
Sign up to request clarification or add additional context in comments.

3 Comments

You're right that a check is needed. A button is an element without children, but an 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!
@Mouser Very good point. For the sake of simplicity I decided not to overcomplicate this particular example, providing that in this case target elements are not supposed to have children.
I get it. There was a question on this site last, that I just thought of in relation to your solution. An click on a link didn't work as expected. This was caused by just such a generic body click script as your solution. This solution should be used with care.
3

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.

answered Jan 2, 2015 at 14:39

4 Comments

No problem. Glad to be of help.
You've got a typo: an extra } before the , false.
won't this add multiple handlers as it targets all .z1 items on each click ?
@GabyakaG.Petrioli Yes it does, when the user clicks the start button multiples times it will add more click handlers to the button.
1

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);
});
answered Jan 2, 2015 at 14:34

Comments

1

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.

answered Jan 2, 2015 at 14:36

1 Comment

"Not directly an answer to this problem," - Not actually an answer to the problem the OP is having at all, given the problem with trying to use .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.)
0

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">

answered Jan 2, 2015 at 19:19

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.