Призначте обробники кліків у циклі for


74

У мене кілька ДІВ - х #mydiv1, #mydiv2, #mydiv3... і хоче призначати обробник клацання до них:

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

Але замість показу 'you clicked 3'при натисканні на #mydiv3(як і для кожного іншого кліку) я отримую 'you clicked 20'. Що я роблю не так?

Відповіді:


135

Поширеною помилкою є створення замикань у циклах у Javascript. Вам потрібно мати якусь функцію зворотного виклику, як це:

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

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

Оновлення від 3 червня 2016 року: оскільки це питання все ще набирає популярності, а ES6 також стає популярним, я б запропонував сучасне рішення. Якщо ви пишете ES6, ви можете використовувати letключове слово, яке робить iзмінну локальною для циклу замість глобальної:

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

Це коротше і легше зрозуміти.


7
Насправді в якості пояснення слід написати, що анонімна функція, що посилається на "i", виконується лише в контексті циклу, який не запускається негайно. Це означає, що коли відбувається подія onclick, "i" фактично перебуває в кінці умови циклу. Ви можете обійти це, передавши "i" як локальний параметр (як це робить це рішення зворотного виклику).
hurikhan77

3
Я б справді не називав callback"зворотний дзвінок". Це не зворотний дзвінок. Це фабрика для створення зворотних викликів.
TJ Crowder,

Дійсно корисна відповідь. Мав і цю проблему, і зміг змінити ваше рішення, щоб створити подію клацання прив’язки кожного разу, коли цикл пройшов.
Ян

завжди буде двадцять. спричинене збільшенням на стороні клієнта.
de_nuit

12

Для уточнення, i дорівнює 20, оскільки подія натискання не спрацює до закінчення циклу.


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

Перехід через анонімну функцію для мене працює краще
Вад

6

Використовуючи on, щоб приєднати обробник 'click', ви можете використовувати дані події, щоб передати ваші дані, як у:

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


4

Ви можете пройти, призначивши один раз обробник клацань (або, принаймні, не роблячи багато зайвих закриттів). Помістіть усі div в один клас mydivs, а потім:

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

Це розглядає ідентифікатор елемента, щоб отримати його номер, використовуючи sliceметод рядка для видалення початкових літер.

Примітка: Може бути, краще використовувати

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

замість

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

2

Як правило, якщо ви хочете призначити дескриптори кліків великій кількості елементів, ви хочете мати контейнер (більш високий рівень div), який інтерпретує кліки для вас, як клацання, що з’являється з купола.

<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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.