开发者

Variable in JavaScript callback functions always gets last value in loop? [duplicate]

开发者 https://www.devze.com 2022-12-24 13:15 出处:网络
This question already has answers here: JavaScript closure inside loops – simple practical exa开发者_如何学Cmple
This question already has answers here: JavaScript closure inside loops – simple practical exa开发者_如何学Cmple (44 answers) Closed 7 years ago.

I'm trying to do the following:

I have a set of images and select (dropdown) HTML elements, 30 of each one. I'm trying to use AddEventListener on a loop from 1 to 30 so that when I change the value of the select, the image src is updated (and the image changes).

The AddEventListener function is this one:

function AddEventListener(element, eventType, handler, capture)
{
    if (element.addEventListener)
        element.addEventListener(eventType, handler, capture);
    else if (element.attachEvent)
        element.attachEvent("on" + eventType, handler);
}

I tried this and it worked:

var urlfolderanimalimages = "http://localhost/animalimages/";
var testselect = "sel15";
var testimg = "i15";

AddEventListener(document.getElementById(testselect), "change", function(e) {
    document.getElementById(testimg).src = urlfolderanimalimages + document.getElementById(testselect).value;
    document.getElementById(testimg).style.display = 'inline';

    if (e.preventDefault) e.preventDefault();
    else e.returnResult = false;
    if (e.stopPropagation) e.stopPropagation();
    else e.cancelBubble = true;
}, false);

But then I tried to call it in a loop and it doesn't work. The event is added, but when I change any select, it will update the last one (the image with id i30).

var urlfolderanimalimages = "http://localhost/animalimages/";

for (k=1;k<=30;k++) {
    var idselect = "sel" + k;
    var idimage = "i" + k;

    AddEventListener(document.getElementById(idselect), "change", function(e) {
        document.getElementById(idimage).src = urlfolderanimalimages + document.getElementById(idselect).value;
        document.getElementById(idimage).style.display = 'inline';

        if (e.preventDefault) e.preventDefault();
        else e.returnResult = false;
        if (e.stopPropagation) e.stopPropagation();
        else e.cancelBubble = true;
    }, false);

}

What am I doing wrong? I'm new to JavaScript (and programming in general), so sorry for the vomit-inducing code :(


The problem is that you are 'closing' over the same variable.

var does not declare a variable1. It simply annotates a 'stop look-up' for an identifier within a given execution context.

Please see Javascript Closures "FAQ Notes" for all the nice details. The sections on 'execution context' and 'scope chain' are most interesting.

The common idiom is perform a double-binding to create a new execution context.

E.g.

var k
for (k = 1; k < 10; k++) {
  setTimeout((function (_k) {
    return function () {
      alert(_k)
    }
  })(k), k * 100)
}

As of JavaScript 5th Edition there is Function.bind (it can also be implemented in 3rd edition such as bind from prototype.js), which can be used as such:

var k
for (k = 1; k < 10; k++) {
  setTimeout((function () {
     alert(this)
  }).bind(k), k * 100)
}

1 This construct is referred to as a Variable Declaration in the ECMAScript specification. However, this statements, as misleading as it may be viewed, is stressed to bring home a point: also see "variable hoisting".


I also found this solution at http://meshfields.de/event-listeners-for-loop/:

var myArr = [0,1,2,3]; 

for (var i = 0; i < myArr.length; i+=1) { 

(function (i) { 

    document.getElementById('myDOMelement' myArr[i]).onclick = function () { 

        if (window.console.firebug !== undefined) { 
            console.log('myDOMelement' myArr[i]); 
        } 
        else { 
                alert('myDOMelement' myArr[i]); 
        } 
        }; 
    }) (i); 
}
0

精彩评论

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

关注公众号