You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 15:00:56