Назначение обработчиков щелчков в цикле for


у меня есть несколько div #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'. Что я делаю не так?

6 67

6 ответов:

Это распространенная ошибка, чтобы создать застежка на петли в 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);
  });
}

Это короче и понятнее.

чтобы уточнить, i равно 20, потому что событие click не будет запущено до тех пор, пока цикл не закончится.

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

вы можете получить с назначением обработчика щелчка один раз (или, по крайней мере, не делая много ненужных замыканий). Поместите все дивы в один класс 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(){

используя on чтобы прикрепить обработчик "click", вы можете использовать данные события для передачи своих данных, например:

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>

Как правило, если вы хотите назначить дескрипторы щелчков для большого количества элементов, вы хотите иметь контейнер (более высокий уровень div), который интерпретирует щелчки для вас, поскольку щелчок всплывает из 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>