add event not working properly with closure in a loop

- 1 answer

Ad

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);
  }
Ad

Answer

Ad

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.

Updated Example

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.

Ad
source: stackoverflow.com
Ad