JavaScript中removeEventListener在click监听器内不生效问题
问题原因
监听移除失效的核心原因是你取错了要操作的DOM节点:
- 你是遍历所有棋盘格
node,给每个node本身绑定的click事件和humanClick回调 - 事件触发时
event.target返回的是你鼠标实际点到的最内层元素,如果node内部有子节点(比如渲染棋子后生成的文本节点、嵌套标签),target拿到的就是这个子节点,而不是当初绑事件的node - 你对着根本没绑定过
humanClick的子节点调用removeEventListener,当然不会有效果,绑在父级node上的监听器始终存在。
另外你贴出的humanClick函数末尾缺少闭合的大括号},属于语法遗漏,运行时也可能抛出异常影响逻辑执行。
顺带说明:你现在用同一个humanClick函数引用做绑定/移除事件的写法是对的,这部分逻辑没有问题,不需要调整。
修复方法
两种方案选任意一种即可:
方案1:替换target为currentTarget(改动最小)
event.currentTarget会始终指向当前绑定事件的那个元素,也就是你当初调用addEventListener的DOM节点,不会因为点到内部子元素发生指向变化,只需要改一行取值代码:
function humanClick(event) { // 仅把这里的event.target替换为event.currentTarget即可 const target = event.currentTarget const nodeId = target.id; BOARD[nodeId] = humanPlayer; target.innerHTML = humanPlayer; if (checkForWinner(BOARD) == null) { aiPlay(BOARD); } target.removeEventListener("click", humanClick); }
方案2:绑定事件时闭包缓存节点引用
在遍历添加监听的时候,直接缓存每个node的引用,回调里不需要靠event对象找节点,从根源上避免target指向错误:
nodes.forEach(node => { node.innerText = null; node.style.background = null; // 为每个节点单独绑定对应回调,闭包持有当前node的引用 function onCellClick() { const nodeId = node.id; BOARD[nodeId] = humanPlayer; node.innerHTML = humanPlayer; if (checkForWinner(BOARD) == null) { aiPlay(BOARD); } // 直接给当前绑定事件的节点移除监听 node.removeEventListener("click", onCellClick); } node.addEventListener("click", onCellClick); });
内容的提问来源于stack exchange,提问作者nick
相关产品推荐
相关产品推荐

