I'm using jQuery and I have a strange thing that I don't understand. I have some code:
for (i = 1; i <= some_number; i++) {
$("#some_button" + i).click(function() {
alert(i);
});
}
"#some_button" as the name says - they are some buttons. When clicked they should pop-up a box with it's number, correct? But they don't. If there is 4 buttons, they always pop-up "5" (buttons count + 1). Why is that so?
Wrap the function outside because for speed and the fact i will keep resetting.
Because in the moment you click them, i == 5.
It has to do with JavaScript scoping. You can get around it easily by introducing another scope by adding a function and having that function call itself and pass in i:
This creates a closure - when the inner function has access to a scope that no longer exists when the function is called. See this article on the MDC for more information.
EDIT: RE: Self-calling functions: A self-calling function is a function that calls itself anonymously. You don't instantiate it nor do you assign it to a variable. It takes the following form (note the opening parens):
Relating this to the question, the body of the anonymous function would be:
Combining these together, we get the answer in the first code block. The anonymous self-calling function is expecting an argument called
j
. It looks for any element with an id ofsome_button
with the integer value ofj
at the end (e.g. some_button1, some_button10). Any time one of these elements is clicked, it alerts the value ofj
. The second-to-last line of the solution passes in the valuei
, which is the loop counter where the anonymous self-calling function is called. Done another way, it might look like this:You are having a very common closure problem in the
for
loop.Variables enclosed in a closure share the same single environment, so by the time the
click
callback is called, the loop will have run its course and thei
variable will be left pointing to the last entry.You can solve this with even more closures, using a function factory:
This can be quite a tricky topic, if you are not familiar with how closures work. You may to check out the following Mozilla article for a brief introduction:
This is very clever code. So clever it's a question on SO. :) I'd sidestep the question altogether by dumbing the code down, just to have a chance at understanding it (or having a colleague understand it) six months from now. Closures have their place, but in this case I'd avoid them in favour of more understandable code.
Probably, I'd attach the same function to all the buttons, which would get the button from the event, strip "some_button" from the ID, and alert the result. Not nearly as pretty, but I guarantee everyone in the office could follow it at a glance.
This is because of how closures work in JavaScript. Each of the 5 functions you are creating is basically sharing the same
i
variable. The value ofi
inside your function is not being evaluated when you are creating the function, but when the click event occurs, by which time the value ofi
is 5.There are various techniques for getting around this (when this behavior isn't what you want). One (if you have a simple function, like you do here) is to use the Function constructor instead of a function literal: