add event not working properly with closure in a loop
The practical problem I've met is just like the testing code below
I need to pack the bunch of event adder in a function, and call it many times
It generate the same result, that is, the event not applying to every element
window.onload = function() {
var body = document.body;
var elText = '<input id="randId" type="text"/>';
var elTmp;
var ids = [];
var times = 3;
var randId = function(){
return Math.random().toString(36).substr(2,9);
};
var setEvent = function(el, text) {
el.addEventListener('click', function(e) {
el.value = text;
});
};
for (var i = 0; i < times; i++) {
ids.push(randId());
body.innerHTML += elText.replace('randId', ids[ids.length - 1]);
elTmp = document.getElementById(ids[ids.length - 1]);
setEvent(elTmp, 'this is ' + (i+1));
}
};
Even wrap those code in a closure, the event only apply to the last created element, the first two are not
Refer to this topic
for (var i = 0; i < times; i++) {
ids.push(randId());
body.innerHTML += elText.replace('randId', ids[ids.length - 1]);
elTmp = document.getElementById(ids[ids.length - 1]);
(function(_i) {
setEvent(elTmp, 'this is ' + (_i+1));
})(i);
}
Answer
The problem is that all of the inner HTML is changing on each iteration. When this happens, all of the previous event listeners are removed (since the elements are removed from the DOM). I just answered a similar question earlier.
According to MDN:
.innerHTML
- Removes all of element's children, parses the content string and assigns the resulting nodes as children of the element.
To work around that, you could use the .insertAdjacentHTML()
method to insert the element instead. In doing so, none of the other HTML is touched/removed since this merely inserts the HTML.
for (var i = 0; i < times; i++) {
ids.push(randId());
body.insertAdjacentHTML('beforeend', elText.replace('randId', ids[ids.length - 1]));
elTmp = document.getElementById(ids[ids.length - 1]);
setEvent(elTmp, 'this is ' + (i+1));
}
Of course, you can also use the .appendChild()
method as well.
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM