web-dev-qa-db-fra.com

Attribuer des gestionnaires de clic dans la boucle

J'ai plusieurs div's #mydiv1, #mydiv2, #mydiv3, ... et souhaitez leur affecter des gestionnaires de clics:

$(document).ready(function(){
  for(var i = 0; i < 20; i++) {
    $('#question' + i).click( function(){
      alert('you clicked ' + i);
    });
  }
});

Mais au lieu de montrer 'you clicked 3' en cliquant sur #mydiv3 (comme pour chaque autre clic) j'obtiens 'you clicked 20'. Qu'est-ce que je fais mal?

70
Philip

C'est une erreur courante de créer fermetures en boucles en Javascript. Vous devez avoir une sorte de fonction de rappel comme celle-ci:

function createCallback( i ){
  return function(){
    alert('you clicked' + i);
  }
}

$(document).ready(function(){
  for(var i = 0; i < 20; i++) {
    $('#question' + i).click( createCallback( i ) );
  }
});

Mise à jour du 3 juin 2016: étant donné que cette question continue de gagner du terrain et que ES6 devient également populaire, je suggérerais une solution moderne. Si vous écrivez ES6, vous pouvez utiliser le mot clé let, qui rend la variable i locale à la boucle au lieu de globale:

for(let i = 0; i < 20; i++) {
  $('#question' + i).click( function(){
    alert('you clicked ' + i);
  });
}

C'est plus court et plus facile à comprendre.

129
Harmen

Pour clarifier, i est égal à 20 car l'événement click n'aura pas été déclenché avant la fin de la boucle.

12
Donald
$(document).ready(function(){
  for(var i = 0; i < 5; i++) {
   var $li= $('<li>' + i +'</li>');
      (function(i) {
           $li.click( function(){
           alert('you clicked ' + i);
         });
      }(i));
      $('#ul').append($li);
  }
});
7
user1490857

En utilisant on pour attacher le gestionnaire 'click' vous pouvez utiliser les données d'événement afin de passer vos données comme dans:

for(var i = 0; i < 20; i++) {
  $('#question' + i).on('click', {'idx': i}, function(e) {
    alert('you clicked ' + e.data.idx);
  });
}
//
// let's creat 20 buttons
//

for(var j = 0; j < 20; j++) {
        $('body').append($('<button/>', {type: 'button', id: 'question' + j, text: 'Click Me ' + j}))
}

//
// Passing data to the handler
//
for(var i = 0; i < 20; i++) {
  $('#question' + i).on('click', {'idx': i}, function(e) {
    console.log('you clicked ' + e.data.idx);
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Vous pouvez vous en tirer en affectant une fois le gestionnaire de clics (ou du moins en ne faisant pas beaucoup de fermetures inutiles). Mettez tous les divs dans une classe mydivs, puis:

$(document).ready(function(){
    $('.mydivs').click(function(){
        // Get the number starting from the ID's 6th character
        // This assumes that the common prefix is "mydiv"
        var i = Number(this.id.slice(5));

        alert('you clicked ' + i);
    });
});

Cela examine l'ID de l'élément pour obtenir son numéro, en utilisant la méthode slice string pour supprimer les lettres initiales.

Remarque: Il peut être préférable d'utiliser

$('#divcontainer').on('click', '.mydivs', function(){

au lieu de

$('.mydivs').click(function(){
4
PleaseStand

En règle générale, si vous cherchez à affecter des poignées de clic à un grand nombre d'éléments, vous souhaitez disposer d'un conteneur (div de niveau supérieur) qui interprète les clics pour vous, car le clic se propage du dom.

<div id="bucket">
    <span class="decorator-class" value="3">
    ...
</div>

<script>
   $(document).ready(function(e){
      $("#bucket").live('click', function(){
         if(e.target).is('span'){
            alert("elementid: " + $(e.target).val());
         }
      }
   }
<script>
2
stancoffyn