addEventListener usando for loop e passando valores

Estou tentando adicionar ouvinte de evento a vários objects usando um loop for, mas acabo com todos os ouvintes visando o mesmo object -> o último.

Se eu adicionar os ouvintes manualmente definindo boxa e boxb para cada instância, isso funciona. Eu acho que é o addEvent for-loop que não está funcionando do jeito que eu esperava. Talvez eu use a abordagem errada completamente.

Exemplo usando 4 da class = “contêiner” Trigger no contêiner 4 funciona da maneira que é suposto. Acionador no container 1,2,3 aciona o evento no container 4, mas somente se o acionador já tiver sido ativado.

Função para executar em clique:

function makeItHappen(elem, elem2){ var el = document.getElementById(elem); el.style.transform = "flip it"; var el2 = document.getElementById(elem2); el2.style.transform = "flip it"; } 

Função de carregamento automático para adicionar os ouvintes:

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } } 

Código HTML

 

some text

some text

some text

some text

Fechamentos! : D

Este código fixo funciona como você pretendia:

 for (var i = 0; i < elem.length; i += 2) { (function () { var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function() { makeItHappen(boxa,boxb); }, false); elem[k].addEventListener("click", function() { makeItHappen(boxb,boxa); }, false); }()); // immediate invocation } 

Por que isso resolve isso?

 for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } 

Na verdade, é JavaScript não estrito. É interpretado assim:

 var i, k, boxa, boxb; for(i=0; i < elem.length; i+=2){ k = i + 1; boxa = elem[i].parentNode.id; boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } 

Por causa do içamento variável , as declarações var são movidas para o topo do escopo. Como o JavaScript não tem escopo de bloco ( for , if , while etc.), eles são movidos para o topo da function. Atualização: a partir do ES6 você pode usar let para obter variables ​​com escopo de bloco.

Quando seu código é executado, acontece o seguinte: no loop for você adiciona os callbacks de clique e atribui boxa , mas seu valor é sobrescrito na próxima iteração. Quando o evento de clique é acionado, o retorno de chamada é executado e o valor de boxa é sempre o último elemento na lista.

Usando um encerramento (fechando os valores de boxa , boxb etc), você boxb o valor ao escopo do manipulador de cliques.


As ferramentas de análise de código, como JSLint ou JSHint , poderão capturar um código suspeito como este. Se você está escrevendo muito código, vale a pena aprender a usar essas ferramentas. Alguns IDEs os incorporam.

Você enfrenta o problema escopo / fechamento como function(){makeItHappen(boxa,boxb);} referências boxa e boxb, em seguida, sempre o último elemento (s).

Para resolver o problema:

 function makeItHappenDelegate(a, b) { return function(){ makeItHappen(a, b) } } // ... elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false); 

Você pode usar a function Binding.You não precisa usar closures.Veja abaixo:

Antes :

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false); elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false); } } 

Depois :

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; elem[i].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false); elem[k].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false); } } 

É por causa de encerramentos.

Veja isto: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

O código de exemplo e seu código são essencialmente os mesmos, é um erro comum para aqueles que não sabem “fechamento”.

Para simplificar, quando você cria uma function de manipulador dentro de addEvents() , ele não apenas acessa a variável i do ambiente addEvents() , mas também “lembra” i .

E porque o seu manipulador “lembra” i , a variável não vou desaparecer depois que addEvents() foi executado.

Assim, quando o manipulador é chamado, ele usará o i mas a variável i é agora, após o for-loop, 3.

Eu também tive esse problema há um tempo atrás. Eu resolvi usando uma function “adiciona” fora do loop, para atribuir events, e funcionou perfeitamente.

Seu script deve parecer.

 function addEvents(){ var elem = document.getElementsByClassName("triggerClass"); for(var i=0; i < elem.length; i+=2){ var k = i + 1; var boxa = elem[i].parentNode.id; var boxb = elem[k].parentNode.id; //- edit ---------------------------| adds(boxa, boxb); } } //- adds function ----| function adds(obj1, obj2){ obj1.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false); obj2.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false); } //- end edit -----------------------| function makeItHappen(elem, elem2){ var el = document.getElementById(elem); el.style.transform = "flip it"; var el2 = document.getElementById(elem2); el2.style.transform = "flip it"; }