Назначить обработчики кликов в цикле 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'. Что я делаю неправильно?

Ответы

Ответ 1

Общей ошибкой является создание закрытия в циклах в 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);
  });
}

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

Здесь отображается идентификатор элемента, чтобы получить его номер, используя slice метод string, чтобы удалить начальные буквы.

Примечание.. Лучше использовать

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

вместо

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

Ответ 5

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

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

//
// let 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>

Ответ 6

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

Ответ 7

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