I’d like to create a list of items dynamically in jQuery, and dynamically bind a click handler to each one, with the variables in scope at the moment the click handler is created.
The code below creates a list of items from 1-6, but the click handler only seems to bind with the value of the final item – the alert always shows 6.
Is this a JavaScript scope problem? How can I bind the alerts with variables from 1-6?
HTML:
<ul id="saved-list">
</ul>
JS:
var all_cookies = [1,2,3,4,5,6];
for (var i = 0; i < all_cookies.length; i++) {
var route_num = all_cookies[i];
var list_item = "<li><a href='#saved-route'>";
list_item += "<p>" + route_num + "</p></a></li>";
var newLi = $(list_item);
newLi.bind('click', function(){
alert(route_num);
});
$('#saved-list').append(newLi);
}
See jsFiddle here: http://jsfiddle.net/n6FU5/
This is a classic JavaScript confusion between “scope” and “curly braces”. It turns out that curly braces have little to do with scope in JavaScript. Scope only arises from functions.
One consequence of this is that variables are always hoisted to the top of the scope, i.e. the nearest function. So your code is exactly equivalent to:
With this in mind, you can see how, since
route_numis the same variable every time through the loop, after the loop is over its value is set to6, and in the future whenever the click handler is called, the6is what you see.One way to fix this is to wrap the contents of your loop in a self-executing anonymous function:
That way, when the variables get hoisted, they will just get hoisted to the top of that inner self-executing function, and so there will be different instances of them for each run through the loop.