Variável em funções de retorno de chamada JavaScript sempre obtém o último valor no loop?

Eu estou tentando fazer o seguinte:

Eu tenho um conjunto de imagens e selecione (dropdown) elementos HTML, 30 de cada um. Eu estou tentando usar AddEventListener em um loop de 1 a 30 para que quando eu alterar o valor da seleção, a imagem src é atualizada (ea imagem muda).

A function AddEventListener é esta:

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

Eu tentei isso e funcionou:

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

Mas então eu tentei chamá-lo em um loop e não funciona. O evento é adicionado, mas quando eu mudo qualquer seleção, ele atualizará o último (a imagem com 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); } 

O que estou fazendo de errado? Eu sou novo em JavaScript (e programação em geral), então sinto muito pelo código indutor de vômito 🙁

O problema é que você está “fechando” sobre a mesma variável .

var não declara uma variável 1 . Ele simplesmente anota um ‘lookup de parada’ para um identificador dentro de um determinado contexto de execução.

Por favor, veja o Javascript Closures “FAQ Notes” para todos os detalhes. As seções sobre ‘contexto de execução’ e ‘cadeia de escopo’ são mais interessantes.

O idioma comum é executar uma binding dupla para criar um novo contexto de execução.

Por exemplo

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

A partir do JavaScript 5th Edition, há o Function.bind (ele também pode ser implementado na 3ª edição, como bind do prototype.js ), que pode ser usado como tal:

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

1 Esta construção é referida como uma Declaração de Variáveis ​​na especificação do ECMAScript. No entanto, esta afirmação, por mais enganosa que possa ser vista, é enfatizada para trazer um ponto para a questão: veja também "içamento variável".

Eu também encontrei esta solução em 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); }