TL;DR: change var x
to let x
I can't really do a better job explaining than Creating closures in loops: A common mistake, but I'll take a shot at rephrasing it.
Compare the output of these two snippets (below). The only difference is var
vs let
. The example demonstrates creating 5 functions in a loop, but does not call them yet. Each function references variables declared inside the loop, outside the loop and in the for
itself. Then, at the end of the loop, we call all the functions to see what we got.
In the first case, the variables outside
, i
(the loop variable) and inside
(declared inside the loop) are all declared with var
. They are the same variable on every iteration of the loop. The inside var is hoisted to the top of the scope (outside the loop).
When we call all the functions we created, we will see that they all refer to the one-and-only instance of each variable, and they all have the value that the variables have after completion of the loop.
let functions = [];
var outside = 0;
for (var i = 0; i < 5; ++i) {
outside = i * 10;
var inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
Output:
40 5 400
40 5 400
40 5 400
40 5 400
40 5 400
In this second example, the variables are all declared with let
. The variable i
declared in the for
and the variable inside
declared inside the body of the loop are different variables on each iteration of the loop. But the outside
variable is declared outside the loop, so there's still only one outside
variable that is used in every iteration of the loop.
When we call all the functions we made this time, we see that each function is displaying a different variable i
and inside
and their values are the value they held during that particular iteration of the loop, because the variables only existed for that iteration of the loop and the function was bound to the instance of the variable that was used for that iteration. But the outside
variable is the same variable every iteration and holds only one value: the value that it has at the end of the loop.
let functions = [];
let outside = 0;
for (let i = 0; i < 5; ++i) {
outside = i * 10;
let inside = i * 100;
functions.push(() => { console.log(outside, i, inside); })
}
functions.map(f => f()); // call all the functions
Output:
40 0 0
40 1 100
40 2 200
40 3 300
40 4 400
In your case, each function binds to the same (one and only) variable x
. If you change your declaration of x
from var x
to let x
then you will get a different variable x
for each iteration of the loop, and the event listener function will be bound to a different x
each time, which will have the value corresponding to that iteration of the loop.
Footnote: Hopefully functions.map(f => f());
is not confusing for you. It just calls all the functions in the array. It is the same as this:
for (var index = 0; index < functions.length; ++index) {
functions[index]();
}