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
相关产品推荐
相关产品推荐

