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

原生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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:57:15