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

React骰子应用首加载按钮为空及标题无障碍报错解决

问题根因
  • 控制台按钮为空报错:你把DOM查询、事件绑定的代码写在了React组件函数的外层,这部分代码会在模块加载时立刻执行,远早于React把组件渲染到页面、挂载真实DOM的时机,此时document.querySelector等方法根本找不到还不存在的按钮元素,拿到的返回值都是null,后续对null执行addEventListener、修改innerHTML自然会抛出空引用错误。除此之外你的代码还有两个隐藏问题:一是切换骰子类型时会重复给掷骰按钮叠加绑定点击事件,没有移除旧的监听,会导致后续点一次掷骰触发多次随机数计算;二是JSX中使用了原生HTML的class属性,React中需要替换为className才能正常生效。
  • 无障碍标题警告:你用来展示掷骰结果的<h1 id="demo"></h1>初始状态没有任何可识别的文本内容,屏幕阅读器无法解析空标题的语义;同时页面内同时存在3个h1标签,不符合页面标题层级规范(单个页面仅允许存在1个h1主标题),也会触发无障碍检测规则。
修复方案

彻底放弃在React逻辑外直接操作DOM的写法,遵循React状态驱动视图的开发模式:

  1. 用useState维护当前选中骰子面数、掷骰结果两个核心状态,所有文本、数值更新都通过修改状态触发,不需要手动调用innerHTML修改内容
  2. 所有事件直接通过JSX的onClick属性绑定,不需要手动查询DOM挂载监听,从根源避免DOM未挂载导致的空值问题
  3. 修正JSX属性错误,将所有class替换为className;调整标题层级,给结果展示区设置初始占位文本,解决空标题的无障碍警告
  4. 统一掷骰逻辑,不需要每次切换骰子就重新绑定事件,点击掷骰按钮时直接根据当前选中的骰子面数生成对应范围的随机数即可,同时补全你代码中已放置按钮但未实现逻辑的d8/d10/d12骰子支持

修复后的完整代码如下:

import React, { useState } from 'react'
import './Dice.css'

const Dice = () => {
  // 状态:当前选中骰子面数,默认选中d20;当前掷骰结果
  const [currentDice, setCurrentDice] = useState(20)
  const [rollResult, setRollResult] = useState(null)

  // 统一掷骰逻辑
  const handleRoll = () => {
    const res = Math.floor(Math.random() * currentDice) + 1
    setRollResult(res)
  }

  // 切换骰子类型
  const switchDice = (sides) => {
    setCurrentDice(sides)
    setRollResult(null)
  }

  // 支持的骰子配置
  const diceOptions = [4,6,8,10,12,20]

  return (
    <>
        <div className="main__div">
            <div className="dices__main">
                <div className="d20">
                    <div className="dice_tw">
                        <h2 className="dice_tw_screen">Значение&nbsp;</h2>
                        <h2 className="dice_tw_screen" id="dice__type">d{currentDice}:</h2> 
                        <h2 className="dice_tw_screen" id="demo">{rollResult ?? '等待掷骰'}</h2> 
                    </div>
                    <button className="dice__button" type="button" onClick={handleRoll}>Кинуть d{currentDice}:</button>
                </div>
    
                <div className="t">
                    {diceOptions.map(sides => (
                      <div className="wrapper" key={sides}>
                          <button 
                            className={`dice__button_1 ${currentDice === sides ? 'active' : ''}`}
                            onClick={() => switchDice(sides)}
                          >
                            d{sides}
                          </button>
                      </div>
                    ))}
                </div>
            </div>
        </div>
    </>
  )
}

export default Dice

修复后不会再出现DOM空值错误,所有标题都有可识别的文本内容,同时解决了重复绑定事件的隐藏问题,代码可维护性也比原生DOM操作的写法更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:51:23