颜色匹配游戏积分计算与颜色校验功能异常问题排查
颜色匹配游戏崩溃问题修复
这是一款颜色匹配游戏,设有目标颜色与颜色网格,点击正确颜色可获得积分。但当前积分计算与颜色校验流程存在问题:初始运行正常,多次点击颜色后会出现错误,最终完全崩溃。
原始代码
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Color Match Chaos</title> <link rel="stylesheet" href="./styles/main.css"> </head> <body> <div id="startScreen"> <button id="startBtn">Start Game</button> </div> <div id="gameScreen" class="hidden"> <div id="targetBox"> <div id="targetClr" class="clrBox"></div> </div> <div> <span id="pointsBox">0</span> / 10 </div> <div id="gridClrs"> <div class="clrBox clrBoxes" id="1"></div> <div class="clrBox clrBoxes" id="2"></div> <div class="clrBox clrBoxes" id="3"></div> <div class="clrBox clrBoxes" id="4"></div> <div class="clrBox clrBoxes" id="5"></div> <div class="clrBox clrBoxes" id="6"></div> <div class="clrBox clrBoxes" id="7"></div> <div class="clrBox clrBoxes" id="8"></div> <div class="clrBox clrBoxes" id="9"></div> <div class="clrBox clrBoxes" id="10"></div> <div class="clrBox clrBoxes" id="11"></div> <div class="clrBox clrBoxes" id="12"></div> <div class="clrBox clrBoxes" id="13"></div> <div class="clrBox clrBoxes" id="14"></div> </div> </div> <div id="endScreen" class="hidden"></div> <script type="module" src="./scripts/main.js"></script> </body> </html>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; } .hidden { display: none; } #gameScreen { flex-direction: column; justify-content: space-between; align-items: center; height: 100vh; } .clrBox { width: 125px; height: 125px; border: 1px dashed black; border-radius: 7px; margin: 3px; transition: all ease 0.1s; } .clrBox:hover { margin-top: -3px; margin-left: -1px; } #targetClr { margin-top: 20px; border: 1px solid black; } #gridClrs { display: grid; grid-template-columns: repeat(7, 1fr); grid-gap: 7px; margin-bottom: 20px; }
JavaScript
// script.js const startBtn = document.getElementById('startBtn'); const startScreen = document.getElementById('startScreen'); const gameScreen = document.getElementById('gameScreen'); const endScreen = document.getElementById('endScreen'); const pointsBox = document.getElementById('pointsBox'); let points = 0; console.log(points) const targetClrBox = document.getElementById('targetClr'); const gridClrs = document.getElementById('gridClrs'); startBtn.addEventListener('click', setupColorGame); function setupColorGame() { startScreen.classList.add('hidden'); gameScreen.classList.remove('hidden'); gameScreen.style.display = 'flex'; initializeGame(); } function initializeGame() { const clrs = [ 'rgb(238, 82, 83)', 'rgb(253, 57, 115)', 'rgb(87, 101, 116)', 'rgb(95, 39, 205)', 'rgb(6, 152, 22)', 'rgb(29, 209, 161)', 'rgb(243, 104, 224)', 'rgb(255, 159, 243)', 'rgb(230, 126, 34)', 'rgb(254, 202, 87)', 'rgb(46, 134, 222)', 'rgb(84, 160, 255)', 'rgb(1, 163, 164)', 'rgb(0, 210, 211)' ]; const targetClr = selectTargetClr(clrs); targetClrBox.style.backgroundColor = targetClr; const shuffledGridClrs = shuffleArray(clrs); setDivBackgroundColors(shuffledGridClrs); addBoxEventListeners(); } function selectTargetClr(clrs) { const clrsIndex = Math.floor(Math.random() * clrs.length); return clrs[clrsIndex]; } function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } let boxes; function setDivBackgroundColors(colors) { boxes = document.querySelectorAll('.clrBoxes'); boxes.forEach((box, index) => { box.style.backgroundColor = colors[index]; }); } function addBoxEventListeners() { boxes.forEach(box => { box.addEventListener('click', () => { if (box.style.backgroundColor === targetClrBox.style.backgroundColor) { points++ pointsBox.innerHTML = points; console.log(points); initializeGame(); } else { initializeGame(); } }); }); }
问题分析
核心问题是重复绑定事件监听器:每次调用initializeGame()时,都会执行addBoxEventListeners(),给所有颜色盒子再次添加点击事件。多次点击后,每个盒子会堆积大量事件监听器,点击一次会触发几十甚至上百次initializeGame()调用,导致浏览器内存占用飙升,最终崩溃。
另外,直接比较style.backgroundColor存在潜在风险:部分浏览器会将rgb格式转换为其他格式(比如十六进制),导致匹配失败。
修复方案
- 只绑定一次事件监听器:将事件绑定移到初始化阶段,避免重复绑定。
- 存储当前目标颜色:用变量保存当前目标颜色字符串,避免依赖
style属性的返回值。 - 优化游戏重置逻辑:重置时只更新颜色,不重新绑定事件。
修改后的JavaScript代码
// script.js const startBtn = document.getElementById('startBtn'); const startScreen = document.getElementById('startScreen'); const gameScreen = document.getElementById('gameScreen'); const endScreen = document.getElementById('endScreen'); const pointsBox = document.getElementById('pointsBox'); let points = 0; let currentTargetColor = ''; // 存储当前目标颜色 const targetClrBox = document.getElementById('targetClr'); const boxes = document.querySelectorAll('.clrBoxes'); // 提前获取所有盒子 startBtn.addEventListener('click', setupColorGame); // 只绑定一次事件监听器 boxes.forEach(box => { box.addEventListener('click', handleBoxClick); }); function setupColorGame() { startScreen.classList.add('hidden'); gameScreen.classList.remove('hidden'); gameScreen.style.display = 'flex'; points = 0; // 重置积分 pointsBox.textContent = points; initializeGame(); } function initializeGame() { const clrs = [ 'rgb(238, 82, 83)', 'rgb(253, 57, 115)', 'rgb(87, 101, 116)', 'rgb(95, 39, 205)', 'rgb(6, 152, 22)', 'rgb(29, 209, 161)', 'rgb(243, 104, 224)', 'rgb(255, 159, 243)', 'rgb(230, 126, 34)', 'rgb(254, 202, 87)', 'rgb(46, 134, 222)', 'rgb(84, 160, 255)', 'rgb(1, 163, 164)', 'rgb(0, 210, 211)' ]; currentTargetColor = selectTargetClr(clrs); targetClrBox.style.backgroundColor = currentTargetColor; const shuffledGridClrs = shuffleArray(clrs); setDivBackgroundColors(shuffledGridClrs); } function selectTargetClr(clrs) { const clrsIndex = Math.floor(Math.random() * clrs.length); return clrs[clrsIndex]; } function shuffleArray(array) { const newArray = [...array]; // 复制原数组避免修改原数据 for (let i = newArray.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newArray[i], newArray[j]] = [newArray[j], newArray[i]]; } return newArray; } function setDivBackgroundColors(colors) { boxes.forEach((box, index) => { box.style.backgroundColor = colors[index]; }); } function handleBoxClick(e) { const clickedColor = e.target.style.backgroundColor; // 统一格式后比较,避免浏览器解析差异 if (normalizeColor(clickedColor) === normalizeColor(currentTargetColor)) { points++; pointsBox.textContent = points; // 达到10分结束游戏 if (points >= 10) { gameScreen.classList.add('hidden'); endScreen.classList.remove('hidden'); return; } } initializeGame(); } // 统一颜色格式,消除浏览器解析差异 function normalizeColor(color) { // 处理rgb格式,去除空格并转为小写 return color.replace(/\s+/g, '').toLowerCase(); }
修复说明
- 将事件监听器绑定移到初始化阶段,每个盒子只绑定一次点击事件,彻底解决事件堆积问题。
- 新增
currentTargetColor变量存储当前目标颜色,避免依赖DOM元素的style属性返回值。 - 添加
normalizeColor函数统一颜色字符串格式,解决浏览器解析rgb格式的差异问题。 - 新增积分达到10分后跳转结束界面的逻辑,完善游戏流程。
内容的提问来源于stack exchange,提问作者hirithik
相关产品推荐
相关产品推荐

