Considering that var
declarations have function scope (or global if declared in the global scope), your code is equivalent to:
var tempS = shapes.slice(),
i, S;
for (i = 0; i < shapes.length; i++) {
S = tempS.pop();
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
}
Now, it is clear that S
is scoped outside of the for
block, and as the event handlers will be triggered after the loop finishes, S
will have the value of the last iteration.
You need to create a new scope, either through a callback factory (as in this related answer) or an IIFE, to trap the current S
value of the iteration.
var tempS = shapes.slice();
for (var i = 0; i < shapes.length; i++) { (function() {//introduces a new scope
var S = tempS.pop(); //now S is scoped inside this iteration's IIFE
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id); //will reference the IIFE scope's S
}, false);
}
}()); }
You can do this in many alternative ways as well, for example, using Array#forEach:
var tempS = shapes.slice();
shapes.forEach(function(S) { //S is now scoped in the forEach callback scope
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
});
Note that this will change the iteration order, but I believe it won't matter for this use case.
In ES6 it will be possible to use let
to create block-scoped variables:
for (var i = 0; i < shapes.length; i++) {
let S = tempS.pop(); //scoped inside the `for` block
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
}
I'm leaving this approach here for future readers, as let
is not ready for production yet. Most recent browsers do have experimental implementations though (see ES6 compat table).