使用循环添加事件监听时,如何正确访问触发事件的元素本身?
问题:循环创建按钮点击事件总是指向最后一个元素
当使用循环批量创建按钮并绑定点击事件后,所有按钮点击后都会隐藏最后一个按钮,而非自身。尝试使用this或self也无法解决这个问题。
原始代码
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <template id="button-template"> <button></button> </template> <script src="./bug.js"></script> </body> </html>
JS代码
const button_template = document.getElementById("button-template") for(let i = 0; i < 10; i++) { clone = button_template.content.cloneNode(true) btn = clone.querySelector("button") btn.addEventListener("click", () => { btn.style.display = "None" }) btn.innerHTML = i.toString() document.querySelector("body").appendChild(clone) console.log("something") }
问题原因
- 全局变量污染:
clone和btn未用let/const声明,属于全局变量。循环结束后,btn始终指向最后一个创建的按钮,箭头函数捕获的是这个全局变量的引用,导致所有点击事件都操作最后一个按钮。 - 箭头函数的
this指向:箭头函数没有独立的this,它的this继承自外层作用域,因此用this无法获取当前点击的按钮元素。
修复方案
方案一:用const声明局部变量,绑定当前迭代的按钮
const button_template = document.getElementById("button-template") for(let i = 0; i < 10; i++) { // 用const声明局部变量,每个循环迭代生成独立作用域 const clone = button_template.content.cloneNode(true) const btn = clone.querySelector("button") btn.addEventListener("click", () => { btn.style.display = "none" // CSS属性值需小写 }) btn.innerHTML = i.toString() document.querySelector("body").appendChild(clone) }
每个循环迭代会创建新的作用域,箭头函数会捕获当前迭代的btn变量,而非最后一个全局变量。
方案二:使用普通函数,通过this访问当前元素
const button_template = document.getElementById("button-template") for(let i = 0; i < 10; i++) { const clone = button_template.content.cloneNode(true) const btn = clone.querySelector("button") // 普通函数作为事件监听时,this指向触发事件的按钮 btn.addEventListener("click", function() { this.style.display = "none" }) btn.innerHTML = i.toString() document.querySelector("body").appendChild(clone) }
普通函数的this在事件触发时会自动指向当前点击的元素,无需额外处理。
方案三:通过事件对象的target属性获取当前元素
const button_template = document.getElementById("button-template") for(let i = 0; i < 10; i++) { const clone = button_template.content.cloneNode(true) const btn = clone.querySelector("button") btn.addEventListener("click", (e) => { // e.target指向触发事件的元素 e.target.style.display = "none" }) btn.innerHTML = i.toString() document.querySelector("body").appendChild(clone) }
事件对象e的target属性始终指向触发事件的具体元素,不受函数类型和变量作用域影响。
内容的提问来源于stack exchange,提问作者AchoDev
相关产品推荐
相关产品推荐

