简易颜色记忆游戏:如何存储首次点击的卡片元素
颜色记忆游戏卡片匹配逻辑问题
我正在开发一款简易颜色记忆游戏,初始状态为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; }
问题分析与修正方案
核心问题
- 变量作用域错误:
firstCard定义在handleCardClik函数内部,每次循环绑定事件时都会重新初始化,且事件触发时会被覆盖,无法持久保存第一次点击的卡片DOM元素。 - 洗牌函数参数缺失:
shuffleArray函数内部使用未定义的array变量,没有接收传入的colors数组。 - 匹配逻辑漏洞:颜色不匹配时,仅修改了第二次点击的卡片,未操作第一次点击的卡片;且错误地将
firstCard赋值为颜色字符串,丢失了DOM元素引用。 - 重复绑定事件:每次点击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
相关产品推荐
相关产品推荐

