如何让点击事件处理函数准确识别用户实际点击的div?
问题根源
这是JavaScript作用域规则导致的经典问题:
- 循环中用
var声明的square是函数级作用域变量,不属于每轮循环的块级范围 - 所有点击事件的回调函数,都共享同一个外层作用域下的
square引用 - 循环执行完全部100次后,
square的值已经被覆盖为最后一次循环创建的编号99的div,因此不管用户点哪个div,触发回调时拿到的都是这个最终值
具体修改方式
优先方案:利用事件对象直接获取点击元素
你写的事件回调本身就携带了事件对象参数e,完全不需要依赖外层循环捕获的square变量:
- 用
e.currentTarget指代绑定了当前点击监听器的DOM元素,比e.target更稳妥,不会被子元素冒泡事件干扰
修改后的事件绑定代码:
squares[i].addEventListener('click', function(e) { // currentTarget 永远指向绑定监听器的目标div,和循环变量无关联 click(e.currentTarget) })
这种写法不依赖外层作用域的变量,从根源避开闭包陷阱,是最健壮的实现方式。
方案2:替换变量声明实现每轮循环变量独立
如果坚持要通过外层闭包捕获对应div的引用,只需要把循环中声明变量的var替换为块级作用域的let即可:
for(let i=0;i<width*height;i++){ // 把原来的 var square 替换为 let square let square = document.createElement('div'); // 后续原有逻辑保持不变 }
let声明的变量只属于当前循环块级作用域,每轮循环都会生成独立的square实例,事件回调捕获的就是对应那轮创建的div,不会被后续循环的赋值覆盖。
兼容老浏览器的方案:用立即执行函数固定变量值
如果项目需要兼容不支持ES6语法的老旧浏览器,可以通过立即执行函数(IIFE)在每轮循环时,把当前的square值固定在独立作用域中:
squares[i].addEventListener('click', (function(currentSquare) { // 立即函数接收当前循环的square作为入参,返回真正的事件回调 return function() { click(currentSquare) } })(square))
这种写法会在每轮循环执行时,把当时的square值传入独立的函数作用域,生成的回调会永久持有对应div的引用,不会被后续循环的覆盖操作影响。
补充注意:直接用数字作为div的id不符合HTML规范,后续通过id获取元素时可能出现兼容性问题,可以把id设置为
square-${i}的格式,比如square.setAttribute('id',square-${i})。
内容的提问来源于stack exchange,提问作者Elena Spirova
相关产品推荐
相关产品推荐

