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

JavaScript网格对战中事件监听器仅触发一次的解决方法

问题描述

我想给两个网格的每个单元格绑定事件监听器,实现以下逻辑:

  • 轮到我方回合(turn === 0)时,点击敌方网格调用func()发起攻击,并切换回合至敌方(turn = 1)
  • 轮到敌方回合(turn === 1)时,点击我方网格调用func()发起攻击,并切换回合回我方(turn = 0)

但当前代码存在问题:事件监听器似乎每个网格仅能生效一次,我方攻击一次、敌方攻击一次后,我方无法再次发起攻击。请问该如何解决?

用户提供的代码:

const cells = document.querySelectorAll("#enemyGrid .cell");
for (let i = 0; i < cells.length; i++) {
    cells[i].addEventListener("click", () => {
        if (turn === 0) {
            //do stuff
            func(); //sets turn=1
        }
    });
}
const cellsMe = document.querySelectorAll("#meGrid .cell");
for (let i = 0; i < cellsMe.length; i++) {
    cellsMe[i].addEventListener("click", () => {
        if (turn === 1) {
            //do stuff
            func(); //sets turn =0
        }

    });
}
解决方案

你的核心逻辑本身没问题,问题大概率出在func()的额外操作上,或者事件绑定的方式不够健壮。以下是具体排查和解决方法:

1. 先排查func()的问题

先把func()临时替换成单纯修改回合值的代码,测试点击是否能正常循环切换:

function func() {
    turn = turn === 0 ? 1 : 0;
    console.log("当前回合:", turn);
}

如果这样能正常触发,说明func()里存在移除事件监听器、禁用单元格点击(比如加了pointer-events: none)或修改DOM结构的操作,导致后续点击失效。需要在func()里补充回合切换时的恢复逻辑,比如敌方回合结束后,重新启用敌方网格的点击能力:

function func() {
    if (turn === 0) {
        // 我方攻击逻辑
        document.querySelectorAll("#enemyGrid .cell").forEach(cell => {
            cell.style.pointerEvents = "none"; // 临时禁用敌方网格点击
        });
        turn = 1;
    } else {
        // 敌方攻击逻辑
        document.querySelectorAll("#meGrid .cell").forEach(cell => {
            cell.style.pointerEvents = "none"; // 临时禁用我方网格点击
        });
        turn = 0;
        // 切换回我方回合时,恢复敌方网格点击
        document.querySelectorAll("#enemyGrid .cell").forEach(cell => {
            cell.style.pointerEvents = "auto";
        });
    }
}

2. 改用事件委托优化绑定

如果单元格存在动态DOM变化(比如攻击后修改样式/结构),单个单元格的事件绑定容易失效。换成事件委托,把事件绑定到网格容器上,利用事件冒泡判断点击目标,这种方式更稳定:

// 敌方网格事件委托
document.querySelector("#enemyGrid").addEventListener("click", (e) => {
    // 确认点击的是单元格,且当前是我方回合
    if (e.target.classList.contains("cell") && turn === 0) {
        // 获取点击的单元格索引(按需使用)
        const cellIndex = Array.from(e.currentTarget.children).indexOf(e.target);
        // 执行攻击逻辑
        // do stuff
        func();
    }
});

// 我方网格事件委托
document.querySelector("#meGrid").addEventListener("click", (e) => {
    if (e.target.classList.contains("cell") && turn === 1) {
        const cellIndex = Array.from(e.currentTarget.children).indexOf(e.target);
        // do stuff
        func();
    }
});

3. 确认turn的作用域

确保turn是全局变量,或者事件监听器和func()能访问到同一个变量实例。如果turn是局部变量,func()的修改无法被事件监听器感知,会导致后续判断一直不成立。

内容的提问来源于stack exchange,提问作者Chiara Busca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:05:28