3

I am looking into some common Javascript programs. The following one adds 4 buttons to the DOM and adds an event listener to each of them:

for(var i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    //function 1
    (function(i){
     btn.addEventListener('click', function(){console.log(i)});
     })(i);

    //function 2 commented
    /*btn.addEventListener('click', (function(i){
     return function(){
     console.log(i);
     }
     })(i));*/
    
    document.body.appendChild(btn);
}

both function 1 and function 2 add event listener to the buttons and work perfectly. I want to know, why the following code does not :-

for(var i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    btn.addEventListener('click', function(){
        console.log('Clicked' + i);
    });
    document.body.appendChild(btn);
}

This code just logs 5 for every button on click. Why is that, I do not understand why it simply does not hold the value for i for each loop?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Stacy J
  • 2,721
  • 15
  • 58
  • 92
  • 1
    in both your example you create a closure immediately executed, so the local value of i is congruent with the expected result, in the third example you wait until the loop is finished then when you click the value of i its ever the maximum value of the iteration – ale Aug 21 '16 at 06:45
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – jisoo shin Aug 21 '16 at 06:45

2 Answers2

2

Javascript has no block scope if you use var. So, using exactly your code, you can have the "expected result" replacing var for let:

for(let i =0;i<5;i++){
    var btn = document.createElement('button');
    btn.appendChild(document.createTextNode('Button' + i));

    btn.addEventListener('click', function(){
        console.log('Clicked' + i);
    });
    document.body.appendChild(btn);
}
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
1

When addEventListener is added to an eventTarget while it is processing the forloop, it does not trigger the listener.

It is triggered later stage, and at that point the for-loop has finished it's execution and updated the value of i.

When you are creating a function inside the for-loop it is creating a closure and binding the value of i

brk
  • 48,835
  • 10
  • 56
  • 78
  • ok..now I understand, but to get a little deeper, why isn't the event listen triggered, why does it wait for the for loop to finish. I can understand when a external call is made to an api to fetch data, the for loop does not wait for the response..but why in case of a event listener – Stacy J Aug 21 '16 at 07:34
  • 1
    _triggering_ is different from _adding_. Inside loop you are adding the event to the target. But triggering happens when you click on it – brk Aug 21 '16 at 07:55