【问题标题】:adding 'click' event listeners in loop [duplicate]在循环中添加“点击”事件侦听器[重复]
【发布时间】:2012-02-13 03:27:13
【问题描述】:

将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

【问题讨论】:

  • 你可以尝试在循环中声明var 吗?或在循环后为 td1 显式尝试相同的代码。听起来很愚蠢,让我们试试吧。
  • @Azodious,是的,已经试过了,结果是一样的
  • 如果明确指定(硬编码),例如td1 工作正常
  • 尝试创建长度为 8 的数组。用 td 对象填充它,然后尝试将侦听器附加到索引。

标签: javascript events loops click listeners


【解决方案1】:

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

【讨论】:

  • 也许是这样。但我预计每个 td:td1,td2,...td8 都会正常填充
【解决方案2】:

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

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

【讨论】:

  • 它工作得非常好。谢谢。关于关闭的更多问题:它是如何发生的,_td 等于 td
  • 闭包只是一个立即执行的函数,因此保留了执行时变量的状态。我们通过将td_td 输入参数传递给函数来执行函数/闭包。
  • 谢谢你的解释。我只是混淆了_tdtd命名。看起来,我可以只使用td
  • 是的,如果能稍微解释一下 _td 是什么以及最后的 (td) 是什么,那就太好了。
  • @HermannIngjaldsson np,_td 只是形成闭包的function 的输入参数,最后的td 是传递给function 的值。它们实际上不必具有不同的名称,例如他们都可以是td。希望有帮助:-)
【解决方案3】:

以下应按预期工作:

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 不错的解决方案。它工作正常
    【解决方案5】:

    发生了什么

    变量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 的后代。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-01
      • 2017-08-30
      • 1970-01-01
      • 2012-07-18
      • 2023-04-03
      • 2019-07-05
      相关资源
      最近更新 更多