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

简易颜色记忆游戏:如何存储首次点击的卡片元素

颜色记忆游戏卡片匹配逻辑问题

我正在开发一款简易颜色记忆游戏,初始状态为6个黑色卡片,点击卡片会从预定义数组中取色并改变颜色,数组会在游戏开始时洗牌。但我在handleCardClick函数的对比逻辑上卡壳了数小时:当两张卡片颜色相同时功能正常,但颜色不同时,只有第二次点击的卡片会变回黑色,首次点击的卡片仍保持彩色。我找不到存储首次点击卡片的正确方式,可能我的实现思路存在问题。


原代码

JavaScript

let colors = ['red', 'green', 'blue', 'red', 'green', 'blue']
const startButton = document.querySelector('.js-start-btn')
const cards = document.querySelectorAll('.js-card')

const shuffleArray = () => {
    var i = 0
      , j = 0
      , temp = null
  
    for (i = array.length - 1; i > 0; i -= 1) {
      j = Math.floor(Math.random() * (i + 1))
      temp = array[i]
      array[i] = array[j]
      array[j] = temp
    }
}


let emptyArr = []

const handleCardClik = () => {
    // Variable to store first click card
    let firstCard
    for (let i = 0; i < cards.length; i++) {
        cards[i]?.addEventListener('click', () => {
            cards[i].style.backgroundColor = colors[i]
            firstCard = cards[i]
            //push colors into an empty array and start comparing them when there are two colors
            emptyArr.push(colors[i])

            if (emptyArr.length === 2) {
            // if colors are same apply them to the cards, clear the array and the storing variable    
                if (emptyArr[0] === emptyArr[1]) {
                    firstCard = emptyArr[0]
                    cards[i].style.backgroundColor = emptyArr[1]
                    firstCard = ''
                    emptyArr = []

            // if colors differ keep them for 1 second, then change back to black       
                } else if (emptyArr[0] !== emptyArr[1]) {
                    firstCard = emptyArr[0]
                    cards[i].style.backgroundColor = emptyArr[1]
                    emptyArr = []
                    setTimeout(() => {
                    firstCard = 'black'
                    cards[i].style.backgroundColor = 'black'                    
                      }, 1000)     
                    firstCard = ''
                 
                }
            }
        })
              
    }
}

const initializeGame = () => {
    shuffleArray(colors)

    for (let i = 0; i < cards.length; i++) {
        cards[i].style.backgroundColor = 'black'
    }

    handleCardClik()
}

startButton?.addEventListener('click', () => {
    initializeGame()   
})

HTML

<body>
    <section class="section-top sec-position">
        <div class="top-wrapper">
            <button class="start-btn js-start-btn">START</button>
        </div>
    </section>
    <section class="section section-main sec-position">
        <div class="main-wrapper">
            <div class="top-row">
                <div class="card js-card"></div>
                <div class="card js-card"></div>
                <div class="card js-card"></div>
            </div>
            <div class="bot-row">
                <div class="card js-card"></div>
                <div class="card js-card"></div>
                <div class="card js-card"></div>
            </div>
        </div>
    </section>
    <section class="section-bot sec-position">
        <span class="win-message">WINNER LOSER</span>
    </section>
    <script src="/scripts.js"></script>
</body>

CSS

.sec-position {
  max-width: 1200px;
  margin: 0 auto 0 auto;
}

.main-wrapper {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding-top: 50px;
  padding-bottom: 50px;

}

.card {
  width: 200px;
  height: 200px;
  gap: 30px;
  background-color: black;
}

.top-row {
  display: flex;
  gap: 30px;
}

.bot-row {
  display: flex;
  gap: 30px;
}

问题分析与修正方案

核心问题

  1. 变量作用域错误:firstCard定义在handleCardClik函数内部,每次循环绑定事件时都会重新初始化,且事件触发时会被覆盖,无法持久保存第一次点击的卡片DOM元素。
  2. 洗牌函数参数缺失:shuffleArray函数内部使用未定义的array变量,没有接收传入的colors数组。
  3. 匹配逻辑漏洞:颜色不匹配时,仅修改了第二次点击的卡片,未操作第一次点击的卡片;且错误地将firstCard赋值为颜色字符串,丢失了DOM元素引用。
  4. 重复绑定事件:每次点击START按钮都会调用handleCardClik,导致卡片绑定多个点击事件,引发逻辑混乱。

修正后的代码

JavaScript

let colors = ['red', 'green', 'blue', 'red', 'green', 'blue']
const startButton = document.querySelector('.js-start-btn')
const cards = document.querySelectorAll('.js-card')

// 修正洗牌函数,接收目标数组参数
const shuffleArray = (array) => {
    let i = array.length - 1;
    while (i > 0) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
        i--;
    }
}

// 全局状态变量,保存点击状态
let firstCard = null;
let secondCard = null;
let isProcessing = false; // 防止处理过程中重复点击

const handleCardClick = (e) => {
    // 跳过正在处理、已匹配或重复点击的卡片
    if (isProcessing || e.target === firstCard || e.target.dataset.matched === 'true') return;

    const card = e.target;
    const index = Array.from(cards).indexOf(card);
    card.style.backgroundColor = colors[index];

    if (!firstCard) {
        // 第一次点击,保存卡片
        firstCard = card;
    } else {
        // 第二次点击,开始对比
        secondCard = card;
        isProcessing = true;

        const firstColor = colors[Array.from(cards).indexOf(firstCard)];
        const secondColor = colors[Array.from(cards).indexOf(secondCard)];

        if (firstColor === secondColor) {
            // 颜色匹配,标记为已匹配
            firstCard.dataset.matched = 'true';
            secondCard.dataset.matched = 'true';
            resetCards();
        } else {
            // 颜色不匹配,1秒后恢复黑色
            setTimeout(() => {
                firstCard.style.backgroundColor = 'black';
                secondCard.style.backgroundColor = 'black';
                resetCards();
            }, 1000);
        }
    }
}

// 统一重置状态
const resetCards = () => {
    firstCard = null;
    secondCard = null;
    isProcessing = false;
}

const initializeGame = () => {
    // 重置所有卡片状态
    cards.forEach(card => {
        card.style.backgroundColor = 'black';
        delete card.dataset.matched;
    });
    // 洗牌颜色数组
    shuffleArray(colors);
    // 先移除旧事件再绑定,避免重复绑定
    cards.forEach(card => {
        card.removeEventListener('click', handleCardClick);
        card.addEventListener('click', handleCardClick);
    });
    resetCards();
}

startButton?.addEventListener('click', initializeGame)

关键优化点

  • 将状态变量提升到全局,确保多次点击中能正确保存卡片引用和处理状态。
  • 添加isProcessing变量,防止在延迟恢复卡片的过程中重复点击,避免逻辑混乱。
  • 新增resetCards函数统一重置状态,代码更简洁易维护。
  • 每次初始化游戏时先移除旧事件再绑定新事件,解决重复绑定问题。
  • 给已匹配的卡片添加data-matched标记,避免重复点击已匹配卡片。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 11:05:23