如何为对象属性函数绑定addEventListener且页面加载时不触发
解决事件监听器加载时触发及DOM操作的问题
我看到你在做文本RPG的菜单生成时遇到了两个关键问题:一是点击函数在页面加载时就触发,二是当前的innerHTML拼接方式可能导致后续事件丢失。咱们一步步来解决:
1. 为什么函数会立即执行?
你现在写的inventory[i].use()是直接调用函数,它会在代码执行到这一行的时候就运行,而不是把函数作为点击回调传递给addEventListener。正确的做法是传递函数的引用(不带括号),但这里还要注意循环中的变量作用域问题——因为你用了var i,它是全局作用域的变量,当点击事件触发时,循环已经结束,i的值会是inventory.length,导致找不到对应的物品。
2. 修复方案(保留生成逻辑,原生JS实现)
我们可以用两种方式解决作用域问题,同时改用更可靠的DOM创建方法(避免innerHTML拼接导致的事件丢失):
方案一:使用let声明循环变量(ES6+)
let在循环中会创建块级作用域,每次迭代的i都是独立的,这样点击时就能正确拿到当前的物品:
var inventory = [ { name: "first" }, { name: "second", use: function(){ alert ("placeholder function for second"); } }, { name: "third" }, ]; function displayInventory(){ const container = document.getElementById("div_name"); // 先清空容器,避免重复加载 container.innerHTML = ''; for(let i = 0; i < inventory.length; i++){ const item = inventory[i]; // 创建大容器div const bigDiv = document.createElement('div'); bigDiv.className = 'big_div'; bigDiv.id = `additional_div_${item.name}`; bigDiv.textContent = `${item.name} div`; // 如果有use方法,添加点击区域 if(item.hasOwnProperty("use")){ const useDiv = document.createElement('div'); useDiv.className = 'item_use'; useDiv.id = `additional_div_use_${item.name}`; useDiv.textContent = 'click here to use item'; // 传递函数引用,此时item是当前迭代的物品 useDiv.addEventListener("click", item.use); bigDiv.appendChild(useDiv); } container.appendChild(bigDiv); } } // 注意:document.onload要赋值函数引用,不是执行 window.onload = displayInventory;
方案二:使用闭包兼容旧环境
如果需要兼容不支持ES6的环境,可以用闭包保存当前的物品:
// ... inventory定义不变 ... function displayInventory(){ const container = document.getElementById("div_name"); container.innerHTML = ''; for(var i = 0; i < inventory.length; i++){ const item = inventory[i]; const bigDiv = document.createElement('div'); bigDiv.className = 'big_div'; bigDiv.id = `additional_div_${item.name}`; bigDiv.textContent = `${item.name} div`; if(item.hasOwnProperty("use")){ const useDiv = document.createElement('div'); useDiv.className = 'item_use'; useDiv.id = `additional_div_use_${item.name}`; useDiv.textContent = 'click here to use item'; // 用闭包包裹,保存当前item的引用 useDiv.addEventListener("click", (function(currentItem){ return function(){ currentItem.use(); } })(item)); bigDiv.appendChild(useDiv); } container.appendChild(bigDiv); } } window.onload = displayInventory;
3. 额外的几个小问题修复
- 你原来的
document.onload应该改成window.onload,或者用DOMContentLoaded事件,因为document.onload的触发时机较晚,且兼容性不如window.onload。 - 每次调用
displayInventory时,先清空容器的innerHTML,避免多次调用重复添加物品。 - 用
createElement和appendChild代替innerHTML拼接,这样不会销毁已有的DOM元素,也能保证事件监听器正常绑定,同时性能更好。
这样修改后,你的物品点击函数就只会在点击时触发,而且生成逻辑完全保留,后续添加更多物品、地图元素时,只需要往对应的数组里添加对象即可。
内容的提问来源于stack exchange,提问作者Ádám Nagy
相关产品推荐
相关产品推荐

