通过each()添加的jQuery事件处理程序似乎覆盖了之前的那些

时间:2013-05-08 08:00:35

标签: javascript jquery closures

这是我第一次使用jquery而且我不理解以下'副作用':

的script.js:

$(function() {
  $("button").each(function() { 
    $t=$(this);                 
    $t.on("click", function() {
      console.log("Id: " + $t.attr('id'));
    });
  });
});

html:

...
<body>
  <button id="de">de</button>
  <button id="en">en</button>
</body>

当我点击“de”按钮然后点击“en”按钮时,日志会给我:

en
en

这似乎是在第二次调用中覆盖侦听器函数的效果,是吗?我该如何避免这种情况?

2 个答案:

答案 0 :(得分:7)

更新后

在您更新的问题中$t是一个全局变量。在JavaScript(通常)中,您在未事先放置var的情况下声明的所有内容都是绑定到窗口的全局变量(在浏览器中)。

您可以修改代码,方法是将$t = ...更改为var $t = ...,这会在处理程序中声明$t


jQuery允许您将点击处理程序附加到多个按钮,而无需使用.each来迭代它们。

您的代码应该可以工作,但是如果您以可能更改this的方式调用处理程序可能会有问题(注意,您可以关闭this(或使用bind)代码中的.each以确保您获得相同的this,您还可以尝试在每个代码中记录$(this).attr('id')

尝试:

$(function(){
  $("button").click(function() {                     
      console.log("Id: " + $(this).attr('id'));
  });
});

Here is a working fiddle

(Your original code works too by the way (fiddle))

在处理程序中,jQuery返回您在$()中包装的实际dom元素,以再次生成jQuery对象。您不需要构建jQuery对象只是为了获取ID,$(this).attr('id')可以替换为this.id

答案 1 :(得分:2)

问题:关闭

您的代码存在$t引入的与闭包相关的错误。会发生的是每个按钮上都安装了一个点击处理程序;所有这些点击处理程序都将按钮称为$t。这意味着在安装处理程序时会引用$t,并在调用处理程序时评估

应该很容易看出它的发展方向:当调用任何处理程序时,循环已经完成,因此$t具有迭代的最后一个按钮的值。实际上,每个按钮都有一个处理程序,就像点击最后一个按钮一样。

如何解决

一种方法是首先避免创建一个闭包:

$(function() {
  $("button").each(function() { 
    $(this).on("click", function() {
      console.log("Id: " + $(this).attr('id'));
    });
  });
});

没有$t,没问题。实际上,您可以将此等效地写为

$(function() {
  $("button").on("click", function() {
    console.log("Id: " + $(this).attr('id'));
  });
});

否则,您需要确保$t 在每次循环迭代时不同$t 。这样做的方法是将$t的范围限制在循环体中。由于JavaScript具有函数范围,因此唯一的方法是在现场引入和调用函数:

$(function() {
  $("button").each(function() { 
    var $t=$(this);
    (function($u) {
      $u.on("click", function() {
        console.log("Id: " + $u.attr('id'));
      });
    })($t);
  });
});

在这个版本中,每个处理程序实际上都依赖于$u,而{I}又不在处理程序之间共享。这解决了封闭问题。

请注意,我也可以命名最里面的函数参数$t(在这种情况下,名称会隐藏迭代范围内的$t);我选择不这样做只是为了清晰。