开发者

Bind click handler with variables in scope when created?

开发者 https://www.devze.com 2023-03-08 22:00 出处:网络
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.

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.


The problem you're having is that your onclick function is returning the value of the variable route_num at the time the link is clicked. route_num is a global variable and the value is left set to 6 when the loop finishes running. So what you need to do is set the function to alert the value of route_num at the time the for loop is running.

The way to do this is to construct the function as a string so that you can append the value of the string to function definition. Here is an example of what I think you were trying to accomplish.

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);

    var fn = new Function("alert('" + route_num + "');");
    newLi.bind('click', fn); 

    $('#saved-list').append(newLi);       
}


That's because of the way javascript works. The variables in scope ARE accessible as you found out, but they are not separate instances for each sub-scope. Therefore your variable's value IS 6 at the time the handler function is called.

The solution is to save route_num in a data attribute and retrieve it inside your click handler.

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.data('routenum', route_num);
    newLi.bind('click', function() {
        alert($(this).data('routenum'));
    });

    $('#saved-list').append(newLi);
}

Here's your fixed fiddle: http://jsfiddle.net/n6FU5/1/

0

精彩评论

暂无评论...
验证码 换一张
取 消