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

重构标准onClick在html标签中的监听器,面临的问题与我的代码:

 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填充了这个循环中的最后一个元素。
循环初始化看起来正确
为什么这么发生?

这里是实时代码

您需要将事件侦听器的分配包装在闭包中,如下所示:

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

发生了什么

variablestd是在你的事件处理程序之外定义的,所以当你点击一个单元格时,你将logging它设置的最后一个值。

更技术上说:每个事件处理函数都是一个闭包 – 一个引用来自外部作用域的variables的函数。

一般的解决scheme

这种问题的一般解决scheme是从包装函数返回事件处理程序,将想要“修复”的variables作为parameter passing:

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

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

更简单的解决scheme:使用this

有一个更简单的select,但是。 在事件处理程序中, 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); }); 

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

所以这里发生的事情是你将variables'td'保存在事件监听器函数的作用域中。 “td”只有一个实例,每次for循环迭代时都会更新。 因此,当for循环完成时,td的值现在被设置为元素'#td7',你的事件处理程序只是loggingtd的当前值。

在上面的例子中,你可以简单地logging“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循环中创build闭包时保持迭代器的答案。 要做到这一点,你想定义一个函数在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时都会创build一个名为“td”的variables的实例,并且该实例将保留在事件侦听器函数的闭包中。 值得注意的是,bind_event中的't'也是一个新的variables。

据我所知,这是因为closures发生…你分配在FOR语句的范围内的事件处理程序。 点击发生时,如果TD的variables在FOR范围内,则将其取为最后一个版本,并将其写入日志。

以下应按预期工作:

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