addEventListener utilisant pour les valeurs de boucle et de passage [dupliquer]

cette question a déjà une réponse ici:

j'essaie d'ajouter l'écouteur d'événements à plusieurs objets en utilisant une boucle for, mais je finis avec tous les écouteurs ciblant le même objet --> le dernier.

si j'Ajoute les écouteurs manuellement en définissant boxa et boxb pour chaque instance, cela fonctionne. Je suppose que c'est la boucle addEvent qui ne fonctionne pas comme je l'espérais. Peut-être que j'utilise la mauvaise approche.

exemple utilisant 4 de la classe="conteneur" La gâchette du container 4 fonctionne comme prévu. Déclencheur sur conteneur 1,2,3 événement déclencheur sur conteneur 4, mais seulement si le déclencheur a déjà été activé.

fonction pour exécuter sur le 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";
}

fonction de lecture automatique pour ajouter les écouteurs:

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

code HTML

<div class="container">
    <div class="one" id="box1">
        <p class="triggerClass">some text</p>
    </div>
    <div class="two" id="box2">
        <p class="triggerClass">some text</p>
    </div>
</div>

<div class="container">
    <div class="one" id="box3">
        <p class="triggerClass">some text</p>
    </div>
    <div class="two" id="box4">
        <p class="triggerClass">some text</p>
    </div>
</div>
62
demandé sur ROMANIA_engineer 2013-10-25 13:32:55

5 réponses

fermetures! : D

ce code fixe fonctionne comme vous le vouliez:

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
}

pourquoi cela la fixe-t-il?

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

est en fait un JavaScript non-strict. C'est interprété comme ceci:

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

en raison de levage variable , les déclarations var sont déplacées vers le haut de la portée. Depuis JavaScript n'a pas champ d'application ( for , if , while etc.), ils sont déplacés vers le haut de la fonction. mise à Jour: comme de l'ES6, vous pouvez utiliser le bouton let pour obtenir bloc d'étendue variables.

quand votre code exécute ce qui suit arrive: dans la boucle for vous ajoutez les callbacks de clic et vous assignez boxa , mais sa valeur est écrasée dans la prochaine itération. Lorsque l'événement click déclenche le rappel s'exécute et la valeur de boxa est toujours le dernier élément dans la liste.

en utilisant une fermeture (fermant les valeurs de boxa , boxb , etc) vous liez la valeur à la portée du gestionnaire de clic.


les outils d'analyse de Code tels que JSLint ou JSHint seront capables de saisir le code suspect comme ceci. Si vous écrivez beaucoup de code, c'est la peine de prendre le temps d'apprendre à utiliser ces outils. Certains IDEs les ont intégrés.

94
répondu Halcyon 2016-02-22 13:09:56

vous faites face au problème de champ d'application/fermeture en tant que function(){makeItHappen(boxa,boxb);} boxa et boxb références puis toujours le dernier élément(s).

pour résoudre le problème:

function makeItHappenDelegate(a, b) {
  return function(){
      makeItHappen(a, b)
  }
}

// ...
 elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false);
11
répondu tenbits 2013-10-25 09:37:49

vous pouvez utiliser la liaison de fonction.Vous n'avez pas besoin de fermetures d'utilisation.Voir ci-dessous:

avant :

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

après :

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);
   }
}
5
répondu msantos 2016-08-15 04:15:03

C'est à cause des fermetures.

Check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

le code échantillon et votre code est essentiellement le même, c'est une erreur courante pour ceux qui ne savent pas"fermer".

pour simplifier, lorsque vous créez une fonction handler à l'intérieur de addEvents() , il ne se contente pas d'accéder à la variable i de l'environnement addEvents() , mais il" se souvient " i .

et parce que votre gestionnaire" se souvient " i , la variable i ne disparaîtra pas après addEvents() a été exécutée.

ainsi quand le gestionnaire est appelé, il utilisera le i mais la variable i est maintenant, après la boucle for, 3.

1
répondu olindk 2016-06-24 16:25:01

j'ai aussi eu ce problème il y a un moment. Je l'ai résolu en utilisant une fonction "adds" en dehors de la boucle, pour assigner des événements, et cela a fonctionné parfaitement.

votre script devrait ressembler.

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";
}
0
répondu blue3d 2016-01-10 02:11:02