admin 管理员组

文章数量: 1086019

Interactive map with buttons in the shape of states, each button has the state abbreviation as an id, when a button/state is clicked I would like to fire the function "stateSelect" and send the state abbreviation with it so I know what's been pressed. Why doesn't the following work?

    var stateList = new Array("AK","AL","AR","AS","AZ","CA","CO","CT","DC","DE","FL","GA","GU","HI","IA","ID",
    "IL","IN","KS","KY","LA","MA","MD","ME","MH","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY",
    "OH","OK","OR","PA","PR","PW","RI","SC","SD","TN","TX","UT","VA","VI","VT","WA","WI","WV","WY");

    for (var i = 0; i < stateList.length; i++) {
        document.getElementById(stateList[i]).addEventListener('mousedown', function() {stateSelect(stateList[i])}, false);
    }

I obviously want to avoid 50 some lines of code but I'm not sure why this simple loop isn't working.

Interactive map with buttons in the shape of states, each button has the state abbreviation as an id, when a button/state is clicked I would like to fire the function "stateSelect" and send the state abbreviation with it so I know what's been pressed. Why doesn't the following work?

    var stateList = new Array("AK","AL","AR","AS","AZ","CA","CO","CT","DC","DE","FL","GA","GU","HI","IA","ID",
    "IL","IN","KS","KY","LA","MA","MD","ME","MH","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY",
    "OH","OK","OR","PA","PR","PW","RI","SC","SD","TN","TX","UT","VA","VI","VT","WA","WI","WV","WY");

    for (var i = 0; i < stateList.length; i++) {
        document.getElementById(stateList[i]).addEventListener('mousedown', function() {stateSelect(stateList[i])}, false);
    }

I obviously want to avoid 50 some lines of code but I'm not sure why this simple loop isn't working.

Share Improve this question asked Jun 19, 2011 at 3:23 StephenStephen 672 silver badges5 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

Because when the handler runs, it looks up the value of i, which is wherever it was after the loop finished.

You need to scope the i variable in a function:

function listenerForI( i ) {
    document.getElementById(stateList[i]).addEventListener('mousedown', function() {stateSelect(stateList[i])}, false);
}
for (var i = 0; i < stateList.length; i++) {
    listenerForI( i );
}

Now the i referenced by the handler will be the parameter to the listenerForI function that was invoked. As such, that i will reference the value that was passed in from the for loop.

You have a scoping issue. Javascript is not block-scoped; it is function-scoped. Basically, you must create a new function whenever you wish to create a new variable in a loop.

The most elegant way to do so is as follows:

stateList.map(function(abbrev){
    $(abbrev).mousedown(function(){stateSelect(abbrev)});
});

If you are not using jQuery, merely replace $(abbrev).mousedown with document.getElementById(abbrev).addEventListener.

(Just to preempt the people who go "map isn't standard"; it is in the javascript ECMA-262 standard 5th edition which has support from all browser vendors. If one is paranoid about supporting older browsers, one can just $.map.)

Here is how one would do so using a for loop; it's a bit uglier but it demonstrates the necessity of creating new closures via functions:

for(var i=0; i<stateList.length; i++)
    (function(i){
        $(stateList[i]).mousedown(...);
    })(i);

Like I said, a bit uglier than necessary; you could also do this which is slightly less ugly, but is basically the same thing:

function createListener(abbrev) {
    $(abbrev).mousedown(...);
}
for(var i=0; i<stateList.length; i++)
    createListener(stateList[i]);

本文标签: Javascript Array addEventListenerStack Overflow