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

颜色匹配游戏积分计算与颜色校验功能异常问题排查

颜色匹配游戏崩溃问题修复

这是一款颜色匹配游戏,设有目标颜色与颜色网格,点击正确颜色可获得积分。但当前积分计算与颜色校验流程存在问题:初始运行正常,多次点击颜色后会出现错误,最终完全崩溃。

原始代码

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格式转换为其他格式(比如十六进制),导致匹配失败。

修复方案

  1. 只绑定一次事件监听器:将事件绑定移到初始化阶段,避免重复绑定。
  2. 存储当前目标颜色:用变量保存当前目标颜色字符串,避免依赖style属性的返回值。
  3. 优化游戏重置逻辑:重置时只更新颜色,不重新绑定事件。

修改后的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:25:55