如何在javascript(jQuery)中分配迭代数组的事件回调

如何在javascript(jQuery)中分配迭代数组的事件回调,javascript,jquery,events,iteration,Javascript,Jquery,Events,Iteration,我正在通过javascript(使用jQuery)生成一个无序列表。每个listitem都必须为“单击”事件接收自己的事件侦听器。但是,我无法将正确的回调连接到正确的项。一个(剥离的)代码示例可能会让事情变得更清楚一些: for(class_id in classes) { callback = function() { this.selectClass(class_id) }; li_item = jQuery('<li></li>')

我正在通过javascript(使用jQuery)生成一个无序列表。每个listitem都必须为“单击”事件接收自己的事件侦听器。但是,我无法将正确的回调连接到正确的项。一个(剥离的)代码示例可能会让事情变得更清楚一些:

for(class_id in classes) {
    callback = function() { this.selectClass(class_id) };
    li_item = jQuery('<li></li>')
                .click(callback);
}
for(类中的类id){
callback=function(){this.selectClass(class_id)};
li_item=jQuery(“
  • ”) 。单击(回调); }
    实际上,在这个迭代中会有更多的内容,但我认为这与这个问题不是很相关。在任何情况下,发生的情况都是回调函数似乎被引用,而不是被存储(复制(&P)。最终结果?当用户单击任何列表项时,它将始终执行
    classes
    数组中最后一个
    class_id
    的操作,因为它在该特定点使用
    callback
    中存储的函数


    我发现了一些肮脏的解决方法(比如在一个封闭的
    a
    元素中解析
    href
    属性),但我想知道是否有一种方法可以以“干净”的方式实现我的目标。如果我的方法是可怕的,请这样说,只要你告诉我为什么:-)谢谢

    为什么不能全部生成它们,然后调用类似

    $(".li_class").click(function(){ this.whatever() };
    
    编辑:

    如果需要添加更多类,只需在循环中创建一个包含所有类名的字符串,并将其用作选择器

    $(".li_class1, .li_class2, etc").click(function(){ this.whatever() };
    

    我的javascript fu非常弱,但据我所知,闭包引用了堆栈上的局部变量(堆栈框架与函数一起传递,同样非常粗略)。您的示例确实不起作用,因为每个函数都保留对同一变量的引用。尝试创建一个不同的函数来创建闭包,即:

    function createClosure(class_id) {
      callback = function() { this.selectClass(class_id) };
      return callback;
    }
    
    然后:

    for(class_id in classes) {
      callback = createClosure(class_id);
      li_item = jQuery('<li></li>').click(callback);
    }
    
    for(类中的类id){
    callback=createClosure(class_id);
    li_item=jQuery('
  • ')。单击(回调); }
    当然,这有点混乱,可能还有更好的方法。

    这是一个经典的“你需要一个结束”问题。下面是它通常的结果

  • 迭代某些值
  • 在该迭代中定义/分配一个使用迭代变量的函数
  • 您了解到每个函数只使用上一次迭代中的值
  • WTF
  • 同样,当你看到这种模式时,它应该立即让你想到“结束”

    扩展您的示例,下面是如何放入闭包的

    for ( class_id in classes )
    {
      callback = function( cid )
      {
        return function()
        {
          $(this).selectClass( cid );
        }
      }( class_id );
      li_item = jQuery('<li></li>').click(callback);
    }
    

    这是做你想做的事的更好更干净的方法

    使用.data()将class_id信息添加到元素中

    然后使用.live()向所有新元素添加一个click处理程序,这样可以避免使用x*click函数

    for(class_id in classes) {
        li_item = jQuery('<li></li>').data('class_id', class_id).addClass('someClass');
    }
    
    //setup click handler on new li's
    $('li.someClass').live('click', myFunction )
    
    function myFunction(){
       //get class_id
       var classId = $(this).data('class_id');
       //do something
    }
    
    for(类中的类id){
    li\u item=jQuery('
  • ')。数据('class\u id',class\u id.)。addClass('someClass'); } //新li上的安装单击处理程序 $('li.someClass').live('click',myFunction) 函数myFunction(){ //获取class\u id var classId=$(this).data('class_id'); //做点什么 }
    或者您可以将类id附加到这些列表项的.data()

    $("<li />").data("class_id", class_id).click(function(){
        alert("This item has class_id "+$(this).data("class_id"));
    });
    
    $(“
  • ”)数据(“类id”,类id)。单击(函数(){ 警报(“此项具有class_id”+$(This).data(“class_id”); });
  • 但是要小心:您正在为每个
    $(“
  • ”)调用创建新的回调函数。我不确定JavaScript实现的细节,但这可能会占用大量内存。 相反,你可以这样做

    function listItemCallback(){
         alert("This item has class_id "+$(this).data("class_id"));
    }
    
    $("<li />").data("class_id", class_id).click(listItemCallback);
    
    函数listItemCallback(){
    警报(“此项具有class_id”+$(This).data(“class_id”);
    }
    $(“
  • ”)数据(“类id”,类id)。单击(listItemCallback);
  • 您的代码:

    for(class_id in classes) {
        callback = function() { this.selectClass(class_id) };
        li_item = jQuery('<li></li>')
                            .click(callback);
    }
    
    for(类中的类id){
    callback=function(){this.selectClass(class_id)};
    li_item=jQuery(“
  • ”) 。单击(回调); }
    这基本上没问题,只是一个问题。变量
    callback
    是全局变量;所以每次循环时,都会覆盖它。将
    var
    关键字放在它的前面,以便在本地对其进行范围分析,这样您就可以了


    编辑注释:它可能不像您所说的那样是全局的,但它超出了for循环的范围。因此,每次循环中变量都是相同的引用。将
    var
    放入循环中会将其范围限定在循环中,每次都创建一个新的引用。

    Hmm。。。我需要为每个列表项“附加”一个不同的类id。我是否错过了使用您的方法实现这一点的简单方法?您将相同的类附加到每个
    li
    将是单击的特定项。我唯一的问题是在同一集合上迭代两次。创建后,再次添加处理程序。看起来很有希望。。。我可能不得不朝这个方向移动,尽管,正如你所说,这仍然“感觉”有点奇怪……正如彼得所说,你遇到了一个关闭问题。关于这个问题的更多信息,斯科特·艾伦(K Scott Allen)在这里有一个不错的帖子:实际上,它看起来像一个简单的范围界定问题。请参阅下面的答案。现在,它是在for-in循环之前使用
    var
    关键字“初始化”的。你的意思是,如果我把这个
    var
    -关键字放在循环中,它会工作得很好吗?差不多。它可能不像您所说的那样是全局的,但它超出了for循环的范围。因此,每次循环中变量都是相同的引用。将var放入循环会将其限定到循环中,每次都创建一个新的引用。酷。我想这就是使用我的方法时的解决方案。然而,live()方式似乎也很有效。有没有“更好”的两个呢?太好了。这实际上是一种非常棒的方式。谢谢你给我指出这一点。现在:它似乎只接受jQuery(cssselector)。若我使用list.children().live(blabla),其中“list”是
      的jQuery对象,那个么它将不起作用。这有什么原因吗?没有用.children()尝试过,但我认为它需要一个实际的选择器,因为它在文档上使用事件委托,然后检查
      for(class_id in classes) {
          callback = function() { this.selectClass(class_id) };
          li_item = jQuery('<li></li>')
                              .click(callback);
      }