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:
var all_cookies = [1,2,3,4,5,6];
var i;
var route_num;
var list_item;
var newLi;
for (i = 0; i < all_cookies.length; i++) {
route_num = all_cookies[i];
list_item = "<li><a href='#saved-route'>";
list_item += "<p>" + route_num + "</p></a></li>";
newLi = $(list_item);
newLi.bind('click', function(){
alert(route_num);
});
$('#saved-list').append(newLi);
}
With this in mind, you can see how, since route_num is the same variable every time through the loop, after the loop is over its value is set to 6, and in the future whenever the click handler is called, the 6 is what you see.
One way to fix this is to wrap the contents of your loop in a self-executing anonymous function:
var all_cookies = [1,2,3,4,5,6];
for (var i = 0; i < all_cookies.length; i++) {
(function () {
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);
}());
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With