在循环中添加“click”事件侦听器

时间:2012-01-18 11:53:11

标签: javascript events loops click listeners

将html标记中的标准onClick重构为侦听器,遇到了我的代码问题:

var td;
    for (var t=1;t<8;t++){
        td = document.getElementById('td'+t);
        if (typeof window.addEventListener==='function'){
                td.addEventListener('click',function(){
                    console.log(td);
            })}
 }  

当点击td元素时,假设点击td来自循环的最后一个索引,例如。 7
看起来,eventListeners仅填充此循环中的最后一个元素 循环初始化看起来是正确的 为什么会这样?

这是live code

5 个答案:

答案 0 :(得分:57)

您需要将事件侦听器的赋值包装在闭包中,例如:

var td;
for (var t = 1; t < 8; t++){
    td = document.getElementById('td'+t);
    if (typeof window.addEventListener === 'function'){
        (function (_td) {
            td.addEventListener('click', function(){
                console.log(_td);
            });
        })(td);
    }
}

答案 1 :(得分:31)

发生了什么

变量td是在事件处理程序之外定义的,因此当您单击一个单元格时,您将记录它设置的最后一个值。

更技术上:每个事件处理函数都是一个闭包 - 一个引用外部作用域变量的函数。

一般解决方案

这类问题的一般解决方案是从包装函数返回事件处理程序,将想要“修复”的变量作为参数传递:

td.addEventListener('click', function(wrapTD) {
    return function() { console.log(wrapTD); }
}(td));

参数现在绑定到调用的包装函数的范围。

更简单的解决方案:使用this

然而,有一个更简单的选择。在事件处理程序中, this设置为定义处理程序的元素,因此您只能使用this而不是td

td.addEventListener('click', function() {
    console.log(this);
});

更简单:没有循环!

最后,你可以完全摆脱for循环并在整个表上设置一个事件处理程序:

var table = document.getElementById('my-table');

table.addEventListener('click', function(e) {
    if (e.target.nodeName.toUpperCase() !== "TD") return;

    var td = e.target;
    console.log(td);
});

对于较大的表,这是一个更好的解决方案,因为您只用一个替换多个事件处理程序。请注意,如果将文本包装在另一个元素中,则需要对其进行调整以检查目标元素是否为td的后代。

答案 2 :(得分:7)

所以这里发生的是你将变量'td'保留在事件监听器函数的范围内。只有1个'td'实例,每次for循环迭代时都会更新。因此,当for循环结束时,td的值现在设置为元素'#td7',而你的事件处理程序只是记录td的当前值。

在上面的示例中,您只需记录'this':

var td;
for (var t=1;t<8;t++){
    td = document.getElementById('td'+t);
    if (typeof window.addEventListener==='function'){
      td.addEventListener('click',function(){
        console.log(this);
      });
    }
}

因为'this'被设置为一个事件被绑定的元素,用于执行事件处理程序。

我猜你正在寻找更多关于在for循环中创建闭包时保持迭代器的答案。为此,您需要在for循环之外定义一个函数。

for (var t=1;t<8;t++){
  bind_event(t);
}

function bind_event(t) {
    var td = document.getElementById('td'+t);
    if (typeof window.addEventListener==='function'){
      td.addEventListener('click',function(){
        console.log(td);
      });
    }
}

这样,每次运行bind_event时都会创建一个名为“td”的变量实例,并且该实例将保留在事件监听器函数的闭包中。值得注意的是bind_event中的't'也是一个新变量。

答案 3 :(得分:1)

据我了解,这是因为闭包...你在FOR语句的范围内分配事件处理程序。当发生单击时,如果TD变量在FOR的范围内,它将采用最后一个版本并将其写入日志。

答案 4 :(得分:0)

以下内容应按预期工作:

  for (var t=1;t<8;t++){
        var td;
        td = document.getElementById('td'+t);
        if (typeof window.addEventListener==='function'){
                td.addEventListener('click',function(){
                    console.log(this);
            })}
 }