非按钮元素Mousedown事件不生效 记忆游戏代码逻辑修正求助
问题修复与代码优化方案
核心错误原因
colorClicked函数仅注册事件监听器,没有返回等待用户点击的Promise,await colorClicked()会直接执行完成,不会等待用户交互;且每次调用该函数都会重复绑定事件,导致后续单次点击触发多次回调。- 主逻辑存在规则漏洞:生成的随机颜色未存入
sequence序列,没有按照记忆游戏规则校验用户输入的完整序列;内层循环变量j最大值为i,永远不可能等于rounds,通关判断条件永远无法触发。
修复代码
第一步:改造事件绑定逻辑(仅在游戏初始化时执行一次,不要放在循环里调用)
// 全局存储点击事件的resolve回调 let resolveUserClick; const colorList = [red, blue, green, yellow]; // 一次性绑定所有颜色块的点击事件 colorList.forEach(colorEl => { colorEl.addEventListener("mousedown", async () => { // 游戏未启动或未处于等待点击状态时不响应 if (!gameOn || !resolveUserClick) return; await flash(colorEl); await revert(); // 返回用户点击的颜色块,结束等待 resolveUserClick(colorEl); resolveUserClick = null; }); }); // 改造为等待用户点击的异步函数 async function colorClicked() { return new Promise(resolve => { resolveUserClick = resolve; }); }
第二步:修正主游戏逻辑
async function eachRounds() { // 每局游戏开始前清空历史序列 sequence = []; const statusEl = document.getElementById("status"); for (let i = 0; i < rounds; i++) { // 每轮生成新的随机颜色,加入序列 const random = Math.floor(Math.random() * array.length); const newColor = array[random]; sequence.push(newColor); // 展示新的颜色 statusEl.innerHTML = `Round ${i + 1} out of ${rounds}`; await flash(newColor); await revert(); // 等待用户复现当前全部序列 let isPass = true; for (let j = 0; j < sequence.length; j++) { const userClick = await colorClicked(); if (userClick !== sequence[j]) { statusEl.innerHTML = "Sorry! You lose!"; gameOn = false; isPass = false; break; } } if (!isPass) break; // 全部通关判断 if (i === rounds - 1) { statusEl.innerHTML = "Congratulations! You win!"; gameOn = false; break; } statusEl.innerHTML = "On to the next round!"; // 可加短延迟再进入下一轮提升体验 await new Promise(resolve => setTimeout(resolve, 800)); } }
代码结构优化建议
- 封装游戏状态:将
gameOn、rounds、sequence、当前轮次等全局变量封装到一个游戏状态对象中,避免全局变量污染,也方便多局游戏重置状态。 - 抽离公共方法:把DOM状态更新、延迟等待等通用逻辑抽成单独函数,减少重复代码。
- 增加边界判断:增加游戏进行中禁止重复启动、点击响应防抖等逻辑,避免异常操作导致逻辑混乱。
内容的提问来源于stack exchange,提问作者Jaykob Walson
相关产品推荐
相关产品推荐

