This is because the function assigned to the click event of each li is bound to the "i" variable used in the loop by a closure. Variable i is incremented to 4 before the loop ends, so that is what each will print.
You could use make the function used in the click handler take a parameter, then use partial function application (or currying) to fix the value of the parameter.
What I was thinking of, was something along the lines of:
// From http://ejohn.org/blog/partial-functions-in-javascript/
Function.prototype.curry = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
return fn.apply(this, args.concat(
Array.prototype.slice.call(arguments)));
};
};
els = document.getElementsByTagName('li');
for(i=0; i < els.length; i++){
els[i].addEventListener('click', function(x){
alert(x);
}.curry(i), false);
} var els = document.getElementsByTagName('li');//use var unless you really really really meant global
for(var i=0; i < els.length; i++){
(function(i) {//creates new scope for each iteration
els[i].addEventListener('click', function(){alert(i);}, false);
})(i);
}i dislike the declaration and immediate execution of an anonymous function. i much prefer to give it a name, move it out of the loop itself and then call it within the loop. it makes reading the code more top-to-bottom.
with this approach, when I get to the anon func declaration, i have to scan down looking for when it is invoked. while indentation helps, i then have to backtrack up to the beginning of the func declaration.. and hopefully i haven't lost track of what the execution context for the anon func is and so forth...
But yeah, if we're gonna be super nitpicky, yeah sure, improve the for loop also, while we're at it.
for (var i = -1; els[++i];) {//given the code in the loop body, this is shorter and faster for (var i = -1; els[++i];) ?
Otherwise, the alert won't trigger on the first li element. var els = document.getElementsByTagName('li');
for(i=0; i < els.length; i++){
var j = i;
els[j].addEventListener('click', function(){alert(j);}, false);
}Your confusion is because of a javascript quirk
for (...) {
var foo = bar
}
is actually the same as var foo
for (...) {
foo = bar
}
Notice how you only ever have one fooWhat you need is:
for (...) {
(function(bar) {
var foo = bar
})(bar)
}
which create a new foo every time you loop