web-dev-qa-db-fra.com

ajouter des écouteurs d'événement 'click' en boucle

Refactoring standard onClick dans la balise html aux auditeurs, confronté à un problème avec mon code:

var td;
    for (var t=1;t<8;t++){
        td = document.getElementById('td'+t);
        if (typeof window.addEventListener==='function'){
                td.addEventListener('click',function(){
                    console.log(td);
            })}
 }  

Lorsque vous cliquez sur l’élément td, il est supposé que vous avez cliqué sur td avec le dernier index de la boucle, par ex. 7
On dirait que, eventListeners a été rempli pour le dernier élément de cette boucle uniquement.
L’initialisation de la boucle semble correcte.
Pourquoi c'est arrivé?

Voici code live

20
sergionni

Vous devez inclure l'attribution de l'écouteur d'événement dans une clôture, par exemple:

var td;
for (var t = 1; t < 8; t++){
    td = document.getElementById('td'+t);
    if (typeof window.addEventListener === 'function'){
        (function (_td) {
            td.addEventListener('click', function(){
                console.log(_td);
            });
        })(td);
    }
}
57
jabclab

Que ce passe-t-il

La variable td étant définie en dehors de vos gestionnaires d'événements, lorsque vous cliquez sur une cellule, vous enregistrez la dernière valeur à laquelle elle a été définie.

Plus techniquement: chaque fonction de gestionnaire d'événements est une fermeture, une fonction qui référence les variables à partir d'une étendue externe.

La solution générale

La solution générale à ce type de problème consiste à renvoyer le gestionnaire d'événements à partir d'une fonction wrapper, en transmettant les variables que vous souhaitez "corriger" en tant qu'arguments:

td.addEventListener('click', function(wrapTD) {
    return function() { console.log(wrapTD); }
}(td));

Les arguments sont maintenant liés à la portée de la fonction wrapper invoquée.

Solution plus simple: utilisez this

Il existe cependant une option encore plus simple. Dans un gestionnaire d'événements,this est défini sur l'élément sur lequel le gestionnaire est défini , vous pouvez donc simplement utiliser this au lieu de td:

td.addEventListener('click', function() {
    console.log(this);
});

Encore plus simple: pas de boucle!

Enfin, vous pouvez vous débarrasser entièrement de la boucle foret définir un seul gestionnaire d'événements sur toute la table:

var table = document.getElementById('my-table');

table.addEventListener('click', function(e) {
    if (e.target.nodeName.toUpperCase() !== "TD") return;

    var td = e.target;
    console.log(td);
});

C'est une bien meilleure solution pour les grandes tables, car vous remplacez plusieurs gestionnaires d'événements par un seul. Notez que si vous placez votre texte dans un autre élément, vous devrez l’adapter pour vérifier si l’élément cible est un descendant de td.

31
Jordan Gray

Donc, ce qui se passe ici, c'est que vous gardez la variable 'td' dans la portée de la fonction écouteur d'événement. Il n'y a qu'une seule instance de 'td', qui est mise à jour chaque fois que la boucle for est itérée. Ainsi, lorsque la boucle for est terminée, la valeur de td est maintenant définie sur l'élément '# td7' et votre gestionnaire d'événements enregistre simplement la valeur actuelle de td.

Dans l'exemple ci-dessus, vous pouvez simplement vous connecter 'this':

var td;
for (var t=1;t<8;t++){
    td = document.getElementById('td'+t);
    if (typeof window.addEventListener==='function'){
      td.addEventListener('click',function(){
        console.log(this);
      });
    }
}

puisque 'this' est défini sur l'élément sur lequel un événement était lié pour l'exécution d'un gestionnaire d'événements.

J'imagine que vous recherchez plus de réponses sur la nécessité de garder l'itérateur en main lors de la création de fermetures à partir d'une boucle for. Pour ce faire, vous voulez définir une fonction en dehors de la boucle for.

for (var t=1;t<8;t++){
  bind_event(t);
}

function bind_event(t) {
    var td = document.getElementById('td'+t);
    if (typeof window.addEventListener==='function'){
      td.addEventListener('click',function(){
        console.log(td);
      });
    }
}

De cette façon, une instance d'une variable nommée 'td' est créée à chaque fois que bind_event est exécutée et cette instance sera conservée en fermeture pour la fonction écouteur d'événements. Il est intéressant de noter que 't' dans bind_event est aussi une nouvelle variable.

7
Will Tomlins

Si j'ai bien compris, cela se produit à cause de la fermeture ... vous affectez un gestionnaire d'événements dans le cadre de l'instruction FOR. Lorsque click se produit, il prend la dernière version si la variable TD est dans la portée de FOR et l'écrit dans le journal. 

1
Alex Dn

ce qui suit devrait fonctionner comme prévu:

  for (var t=1;t<8;t++){
        var td;
        td = document.getElementById('td'+t);
        if (typeof window.addEventListener==='function'){
                td.addEventListener('click',function(){
                    console.log(this);
            })}
 } 
0
Evert