Sto cercando di aggiungere listener di eventi a più oggetti utilizzando un ciclo for, ma alla fine tutti gli ascoltatori hanno come target lo stesso object -> l’ultimo.
Se aggiungo manualmente gli ascoltatori definendo boxa e boxb per ogni istanza, funziona. Immagino che sia l’addEvent for-loop che non funziona come speravo. Forse uso l’approccio sbagliato del tutto.
Esempio usando 4 del class = “contenitore” Trigger sul contenitore 4 funziona come dovrebbe. Attivare l’evento di trigger contenitore 1,2,3 sul contenitore 4, ma solo se il trigger è già stato triggersto.
Funzione da eseguire al clic:
function makeItHappen(elem, elem2){ var el = document.getElementById(elem); el.style.transform = "flip it"; var el2 = document.getElementById(elem2); el2.style.transform = "flip it"; }
Funzione di caricamento automatico per aggiungere gli ascoltatori:
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); } }
Codice HTML
some text
some text
some text
some text
Chiusure! : D
Questo codice fisso funziona come previsto:
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 }
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); }
È in realtà non-strict JavaScript. È interpretato in questo modo:
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); }
A causa del sollevamento variabile , le dichiarazioni var
vengono spostate nella parte superiore dell'ambito. Poiché JavaScript non ha scope di blocco ( for
, if
, while
ecc.) Vengono spostate all'inizio della funzione. Aggiornamento: a partire da ES6 è ansible utilizzare let
per ottenere variabili con scope a blocchi.
Quando viene eseguito il codice, accade quanto segue: nel ciclo for
si aggiungono i richiami di clic e si assegna boxa
, ma il suo valore viene sovrascritto nella successiva iterazione. Quando viene boxa
l'evento click, viene eseguito il callback e il valore di boxa
è sempre l'ultimo elemento nell'elenco.
Usando una chiusura (chiudendo i valori di boxa
, boxa
, ecc.) Si associa il valore all'ambito del gestore di clic.
Gli strumenti di analisi del codice come JSLint o JSHint saranno in grado di catturare codice sospetto come questo. Se stai scrivendo molto codice, vale la pena dedicare del tempo a imparare come utilizzare questi strumenti. Alcuni IDE li hanno integrati.
function(){makeItHappen(boxa,boxb);}
problema scope / closure come function(){makeItHappen(boxa,boxb);}
riferimenti boxa e boxb sono quindi sempre gli ultimi elementi.
Per risolvere il problema:
function makeItHappenDelegate(a, b) { return function(){ makeItHappen(a, b) } } // ... elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false);
Puoi utilizzare Function Binding. Non hai bisogno di usare chiusure. Vedi sotto:
Prima :
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); } }
Dopo :
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); } }
È a causa delle chiusure.
Controlla questo: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake
Il codice di esempio e il codice sono essenzialmente gli stessi, è un errore comune per coloro che non conoscono la “chiusura”.
Per dirla in modo semplice, quando crei una funzione di gestore all’interno di addEvents()
, non accede solo alla variabile i
dall’ambiente di addEvents()
, ma anche “ricorda” i
.
E poiché il tuo gestore “ricorda” i
, la variabile non scomparirà dopo l’esecuzione di addEvents()
.
Quindi, quando viene chiamato il gestore, utilizzerà la i
ma la variabile i
è ora, dopo il ciclo for, 3.
Ho anche avuto questo problema qualche tempo fa. L’ho risolto utilizzando una funzione “aggiungi” al di fuori del ciclo, per assegnare eventi e ha funzionato perfettamente.
La tua sceneggiatura dovrebbe assomigliare.
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"; }