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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8909652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 08:03:00  来源:igfitidea点击:

adding 'click' event listeners in loop

javascripteventsloopsclicklisteners

提问by sergionni

Refactoring standard onClickwithin html tag to listeners ,faced problem with my code:

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

When tdelement is clicked on,it's assumed that clicked tdwith last index from loop,e.g. 7
Looks like ,eventListenersbeen populated for last element in this loop only.
Loop initialization looks correct.
Why so happened?

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

Here is live code

这是实时代码

回答by jabclab

You need to wrap the assignment of the event listener in a closure, something like:

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

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

回答by Jordan Gray

What's happening

发生了什么

The variable tdis defined outside of your event handlers, so when you click on a cell you are logging the last value it was set to.

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

More technically: each event handler function is a closure—a function that references variables from an outer scope.

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

The general solution

一般解决方案

The general solution to this kind of problem is to return the event handler from a wrapper function, passing the variables you want to "fix" as arguments:

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

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

The arguments are now bound to the scope of the invoked wrapper function.

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

Simpler solution: use this

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

There's an even simpler option, however. In an event handler, thisis set to the element on which the handler is defined, so you can just use thisinstead of td:

然而,还有一个更简单的选择。在事件处理程序中,this设置为定义处理程序的元素,因此您可以使用this代替td

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

Even simpler: no loop!

更简单:没有循环!

Finally, you can get rid of the forloop entirelyand set a single event handler on the whole table:

最后,您可以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);
});

This is a much better solution for larger tables, since you are replacing multiple event handlers with just one. Note that if you wrap your text in another element, you will need to adapt this to check if the target element is a descendant of a td.

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

回答by Will Tomlins

So what's happening here is that you are keeping the variable 'td' in scope for the event listener function. There is only 1 instance of 'td', and that is getting updated each time the for loop iterates. Thus, when the for loop finished, the value of td is now set to the element '#td7' and your event handler is simply logging the current value of td.

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

In the example above, you could simply log 'this':

在上面的示例中,您可以简单地记录“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);
      });
    }
}

since 'this' is set to the element an event was bound on for the execution of an event handler.

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

I'm guessing you're looking for more of an answer about keeping hold of the iterator when creating closures from within a for loop. To do this you want to define a function outside of the for loop.

我猜你正在寻找更多关于在 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);
      });
    }
}

This way, an instance of a variable named 'td' is created each time bind_event is run, and that instance will be kept in closure for the event listener function. It's worth noting that 't' in bind_event is also a new variable.

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

回答by Alex Dn

As i understand, it happens because of closure...you assign event handler within scope of FOR statement. When click happens, it takes the last version if TD variable in scope of FOR and writes it to log.

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

回答by Evert

the following should work as expected:

以下应该按预期工作:

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