Closures in a for loop
Solution 1:
See the bind method.
$('#button'+i).bind('click', {button: i}, function(event) {
foo(event.data.button);
});
From the docs:
The optional eventData parameter is not commonly used. When provided, this argument allows us to pass additional information to the handler. One handy use of this parameter is to work around issues caused by closures
Solution 2:
Try this code:
function foo(val) {
alert(val);
}
var funMaker = function(k) {
return function() {
foo(k);
};
};
for (var i = 0; i < 3; i++) {
$('#button'+i).click(funMaker(i));
}
Some important points here:
- JavaScript is function scoped. If you want a new ('deeper') scope, you need to create a function to hold it.
- This solution is Javascript specific, it works with or without jQuery.
- The solution works because each value of
i
is copied in a new scope ask
, and the function returned fromfunMaker
closes aroundk
(which doesn't change in the loop), not aroundi
(which does). - Your code doesn't work because the function that you pass to
click
doesn't 'own' thei
, it closes over thei
of its creator, and thati
changes in the loop. - The example could have been written with
funMaker
inlined, but I usually use such helper functions to make things clearer. - The argument of
funMaker
isk
, but that makes no difference, it could have beeni
without any problems, since it exists in the scope of the functionfunMaker
. - One of the clearest explanation of the 'Environment' evaluation model is found in 'Structure and Interpretation of Computer Programs', by Sussman & Abelson (http://mitpress.mit.edu/sicp/ full text available online, not an easy read) - see section 3.2. Since JavaScript is really Scheme with C syntax, that explanation is OK.
EDIT: Fixed some punctuation.
Solution 3:
@Andy solution is the nicest. But you can also use Javascript scoping to help you save the value in your closure.
You do so by creating a new scope in your loop body by executing an anonymous function.
for (var i = 0; i < 3; i++) {
(function(){
var index = i;
$('#button'+index).click(function(){
foo(index);
});
})();
}
Since the loop body is a new scope at each iteration, the index variable is duplicated with the correct value at each iteration.
Solution 4:
Use the .each function from jquery - I guess you a looping through similar elements - so add the click using something like:
$(element).children(class).each(function(i){
$(this).click(function(){
foo(i);
});
});
Not tested but I always use this kind structure where possible.