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

IT技术 javascript events loops click listeners
2021-01-16 15:10:56

onClickhtml 标记内的标准重构为侦听器,我的代码遇到了问题:

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
Looks like ,eventListeners只为这个循环中的最后一个元素填充。
循环初始化看起来正确。
为什么会这样?

这是实时代码

5个回答

您需要将事件侦听器的分配包装在一个闭包中,例如:

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);
    }
}
谢谢你的解释。我只是混淆_tdtd命名。看起来,我可以用td
2021-03-22 15:10:56
@HermannIngjaldsson np,the_td只是function形成闭包的 的输入参数,td最后是传递给function. 它们实际上不必具有不同的名称,例如它们都可以是td. 希望有帮助:-)
2021-03-25 15:10:56
是的,如果能稍微解释一下 _td 是什么以及最后的 (td) 是什么,那就太好了。
2021-03-31 15:10:56
闭包只是一个立即执行的函数,因此在执行时保留了变量的状态。我们通过传递执行函数/关闭td_td输入参数的功能。
2021-04-04 15:10:56
它绝对有效。谢谢。关于关闭的更多问题:它是如何发生的,这_td等于td
2021-04-11 15:10:56

发生了什么

该变量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.

所以这里发生的事情是您将变量“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' 也是一个新变量。

很好的解决方案。它工作正常
2021-03-27 15:10:56

据我了解,这是由于关闭而发生的……您在 FOR 语句的范围内分配了事件处理程序。当点击发生时,如果 TD 变量在 FOR 范围内,它将采用最后一个版本并将其写入日志。

也许,它是。但我预计每个 td:td1,td2,...td8 都会填充好
2021-04-02 15:10:56

以下应该按预期工作:

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