原生JS记忆游戏分数与获胜提示显示失效问题求助
原生JS记忆卡牌游戏功能异常修复
问题根因
你遇到的空指针报错、分数/获胜提示不显示问题,来自3处代码错误:
- 脚本执行时机早于DOM渲染:JS运行时id为
grid的容器还没加载,document.getElementById('grid')返回null,调用appendChild时直接抛出Cannot read properties of null错误 - 结果元素选择器使用错误:用
querySelectorAll('#result')获取元素时,返回的是NodeList集合而非单个DOM节点,直接对集合设置innerHTML不会触发页面更新 - 重复点击同一张卡的分支缺少终止逻辑:判断到用户点击同一张卡后,代码没有中断执行,会继续走入匹配失败分支,触发多余弹窗、重复修改图片属性
前置要求
HTML中必须存在对应容器节点,示例结构:
<h3>得分:<span id="result"></span></h3> <div id="grid"></div> <!-- 脚本放到body闭合标签前,或者用DOMContentLoaded包裹逻辑 --> <script src="your-game.js"></script>
修复后完整代码
const cardArray = [ { name: 'fries', Img: 'images/fries.png' }, { name: 'donut', Img: 'images/donut.png' }, { name: 'hamburger', Img: 'images/hamburger.png' }, { name: 'ramen', Img: 'images/ramen.png' }, { name: 'pizza', Img: 'images/pizza.png' }, { name: 'honey', Img: 'images/honey.png' }, { name: 'meat', Img: 'images/meat.png' }, { name: 'fries', Img: 'images/fries.png' }, { name: 'donut', Img: 'images/donut.png' }, { name: 'hamburger', Img: 'images/hamburger.png' }, { name: 'ramen', Img: 'images/ramen.png' }, { name: 'pizza', Img: 'images/pizza.png' }, { name: 'honey', Img: 'images/honey.png' }, { name: 'meat', Img: 'images/meat.png' }, ] // 替换原有的不严谨洗牌逻辑,洗牌结果更均匀 function shuffleArray(arr) { for (let i = arr.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]] } return arr } shuffleArray(cardArray) // 等DOM完全加载后再执行初始化逻辑 document.addEventListener('DOMContentLoaded', () => { const gridDisplay = document.getElementById('grid') // 修复:获取单个DOM元素用于分数展示 const resultDisplay = document.getElementById('result') let cardChosen = [] let cardChosenIds = [] const cardsWon = [] // 初始化卡牌棋盘 function createBoard() { for (let i = 0; i < cardArray.length; i++) { const card = document.createElement('img') card.setAttribute('src', 'images/blank.png') card.setAttribute('data-id', i) card.addEventListener('click', flipCard) gridDisplay.appendChild(card) } } createBoard() // 卡牌匹配校验 function checkMatch() { const allCards = document.querySelectorAll('img') const firstCardId = cardChosenIds[0] const secondCardId = cardChosenIds[1] // 点击同一张卡的分支,处理完直接终止后续逻辑 if (firstCardId === secondCardId) { alert('不能点击同一张卡牌!') allCards[firstCardId].setAttribute('src', 'images/blank.png') cardChosen = [] cardChosenIds = [] return } if (cardChosen[0] === cardChosen[1]) { alert('匹配成功!') allCards[firstCardId].setAttribute('src', 'images/white.png') allCards[secondCardId].setAttribute('src', 'images/white.png') allCards[firstCardId].removeEventListener('click', flipCard) allCards[secondCardId].removeEventListener('click', flipCard) cardsWon.push(cardChosen) } else { allCards[firstCardId].setAttribute('src', 'images/blank.png') allCards[secondCardId].setAttribute('src', 'images/blank.png') alert('匹配失败,再试一次') } // 重置选中状态,更新分数 cardChosen = [] cardChosenIds = [] resultDisplay.textContent = cardsWon.length // 全部匹配完成展示获胜提示 if (cardsWon.length === cardArray.length / 2) { resultDisplay.textContent = '恭喜你!找到了所有配对卡牌!' } } // 卡牌翻转逻辑 function flipCard() { const cardId = this.getAttribute('data-id') cardChosen.push(cardArray[cardId].name) cardChosenIds.push(cardId) this.setAttribute('src', cardArray[cardId].Img) if (cardChosen.length === 2) { setTimeout(checkMatch, 500) } } })
修改点说明
- 所有DOM操作逻辑放入
DOMContentLoaded回调,保证元素加载完成后再执行代码,解决空指针报错 - 替换结果元素的获取方式,直接获取单个DOM节点,分数和获胜提示可正常渲染
- 重复点击同卡分支增加
return终止逻辑,避免多余执行 - 优化洗牌算法,避免原
0.5 - Math.random()写法导致的洗牌概率不均问题 - 用
textContent替代innerHTML设置纯文本内容,安全性更高
内容的提问来源于stack exchange,提问作者00Chai
相关产品推荐
相关产品推荐

