You can use a for
loop, but you should make sure the loop counter's value gets into a correct scope for click
event handler:
var clickHandler = function(k) {
return function() {
$('#list').fadeOut(450);
$('#q' + k).delay(600).fadeIn(450);
};
};
for (var i = 1; i < 14; ++i) {
$('#p' + i).click(clickHandler(i));
}
Otherwise the delay
and fadeIn
would get applied to #q13
element exclusively, since actual counter (with its final value of 13) would get into closure.
EDIT: Since quite a lot of answers got it wrong here, I'll attempt to explain more precisely what's going on in this code, as it seems to be pretty confusing.
The "natural" solution with injecting the click handler directly into loop would be the following:
for(var i = 1; i < 14; i++) {
$('#p'+i).click(function() {
$('#list').fadeOut(450);
$('#q'+i).delay(600).fadeIn(450)
});
}
But this is not at all equivalent to the extended form, which lists all the 13 variants one after another. The problem is that while there are indeed 13 functions created here, they are all closed over the same variable i
, whose value changes. It finally arrives at the value of 13
and the loop ends.
Some time later the functions attached to #p1
...#p13
elements are called (when one of those elements are clicked) and they use that final value of i
. This results in only #q13
being animated.
What needs to be done here is to do something called lambda lifting and eliminate the free variable i
, whose value gets inadvertly changed. A common technique for that is to provide a "factory function" which accepts value for our variable and outputs an actual function which we'll use as event handler:
var clickHandler = function(k) {
return function() {
$('#list').fadeOut(450);
$('#q' + k).delay(600).fadeIn(450);
};
};
Since the scope of k
parameter is local to clickHandler
, every call to clickHandler
gets different k
variable. The function returned from clickHandler
is therefore closed over different variables, which can in turn have different values. This is exactly what we need. We can then call clickHandler
from our loop, passing it the counter's value:
for (var i = 1; i < 14; ++i) {
$('#p' + i).click(clickHandler(i));
}
I hope this makes the difference somewhat clearer.
EDIT: As Esailija pointed out in the comments, it is also possible to use jQuery.each
to achieve similar effect:
$.each(new Array(13), function(idx) {
$('#p' + (idx + 1)).click(function() {
$('#list').fadeOut(450);
$('#q' + idx).delay(600).fadeIn(450);
});
});
This is probably the solution of choice if you're already aware of the closure/scoping issue I've tried to outline above.