循环绑定的事件监听器均返回相同值,如何排查与修复?
问题分析与修复方案
你编写的updateInventory()函数在循环绑定点击事件时出现所有监听器返回值相同的问题,核心原因是变量作用域导致的共享引用:
问题根源
循环里用var element声明变量,var是函数级作用域,整个循环过程中只会存在一个element变量。当循环结束后,所有点击事件触发时,element已经指向最后一次循环创建的DOM元素,所以不管点击哪个元素,都会调用最后那个元素的id值传入selectBlock()。
修复方案
方案一:将var改为let(最简单高效)
let是块级作用域,每次循环迭代都会创建独立的element变量,每个事件监听器会绑定当前循环对应的元素,不会互相干扰。
修改后的关键代码片段:
for (let i = 0; i < this.gameState.inventory.length; i = i + 2) { if ( this.gameState.inventory[i + 1] != 0 && this.gameState.inventory[i] != undefined ) { // 把var替换成let let element = document.createElement("div"); element.classList.add("inven-box"); element.id = this.gameState.inventory[i]; element.innerHTML = `<span class="inven-text">${ this.gameState.inventory[i] }</span>: <span class="value">${ this.gameState.inventory[i + 1] }</span>`; element.addEventListener("click", function () { alert(this.selectBlock(element.id)); }.bind(this)); document.getElementById("inven-slots").appendChild(element); } }
方案二:通过事件对象获取当前元素
不依赖外部变量,直接从事件源获取点击的元素,彻底规避作用域问题:
// 修改事件绑定部分 element.addEventListener("click", function (event) { // 用event.currentTarget拿到触发事件的当前元素 alert(this.selectBlock(event.currentTarget.id)); }.bind(this));
方案三:闭包封装变量(兼容旧浏览器)
如果需要兼容不支持let的老旧环境,用立即执行函数创建独立作用域:
// 修改事件绑定部分 element.addEventListener("click", (function(el) { return function() { alert(this.selectBlock(el.id)); }; }(element)).bind(this));
内容的提问来源于stack exchange,提问作者Vatsa Pandey
相关产品推荐
相关产品推荐

