在for循环中分配点击处理程序

77

我有多个div,包括#mydiv1#mydiv2#mydiv3等,我希望为它们分配点击处理程序:


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

然而,当我点击#mydiv3时(和其他点击一样),它没有显示'you clicked 3',而是显示'you clicked 20'。我做错了什么?

6个回答

140

在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 ) );
  }
});

更新于2016年6月3日:由于这个问题仍然受到关注,而ES6也越来越流行,我建议使用一种现代的解决方案。如果您编写ES6代码,可以使用let关键字,它使i变量在循环中成为局部变量而不是全局变量:

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

这样更短,更易于理解。


7
实际上,需要解释的是匿名函数引用的变量"i"只在循环上下文中执行,而不是立即运行。这意味着,当点击事件发生时,"i"实际上处于循环结束条件。您可以通过将"i"作为本地参数传递(就像这个回调解决方案所做的那样)来避免这种情况。 - hurikhan77
3
我真的不会把 callback 叫做"回调"。它不是一个回调,而是创建回调的工厂。 - T.J. Crowder
非常有用的答案。我也遇到了这个问题,能够修改您的解决方案,使每次循环通过时都创建一个绑定点击事件。 - Ian
这将始终是二十。由客户端增量引起的。 - 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);
  }
});

通过匿名函数传递更适合我。 - Vad

6
使用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>


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

这段代码通过查找元素的ID来获取其编号,使用slice字符串方法去掉初始字母。

注意:最好使用

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

替代

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

2

通常,如果您要为大量项目分配单击处理程序,则需要使用一个容器(较高级别的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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接