I have some jQuery code that I would like reviews and pointers on on how to bring its line count down and shorten.
$('#p1').click(function() {
$('#list').fadeOut(450);
$('#q1').delay(600).fadeIn(450)
});
$('#p2').click(function() {
$('#list').fadeOut(450);
$('#q2').delay(600).fadeIn(450)
});
$('#p3').click(function() {
$('#list').fadeOut(450);
$('#q3').delay(600).fadeIn(450)
});
$('#p4').click(function() {
$('#list').fadeOut(450);
$('#q4').delay(600).fadeIn(450)
});
...
$('#p12').click(function() {
$('#list').fadeOut(450);
$('#q12').delay(600).fadeIn(450)
});
$('#p13').click(function() {
$('#list').fadeOut(450);
$('#q13').delay(600).fadeIn(450)
});
Can this code be better optimised? Or at least made less verbose?
You can use a
forloop, but you should make sure the loop counter’s value gets into a correct scope forclickevent handler:Otherwise the
delayandfadeInwould get applied to#q13element 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:
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 of13and the loop ends.Some time later the functions attached to
#p1…#p13elements are called (when one of those elements are clicked) and they use that final value ofi. This results in only#q13being 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:Since the scope of
kparameter is local toclickHandler, every call toclickHandlergets differentkvariable. The function returned fromclickHandleris therefore closed over different variables, which can in turn have different values. This is exactly what we need. We can then callclickHandlerfrom our loop, passing it the counter’s value:I hope this makes the difference somewhat clearer.
EDIT: As Esailija pointed out in the comments, it is also possible to use
jQuery.eachto achieve similar effect:This is probably the solution of choice if you’re already aware of the closure/scoping issue I’ve tried to outline above.