屏幕阅读器只认语义化标签(如)而不认,因其不解析css类名,仅依赖原生语义或显式aria角色;自带role="navigation"并被主流读屏器识别为可跳转地标,而需手动补全role、tabindex等属性才能具备同等可访问性。

为什么屏幕阅读器只认 <nav></nav> 不认 <div class="nav">
<p>因为屏幕阅读器不解析 CSS 类名,也不猜你“想表达什么”。它只信任原生语义标签或显式声明的 ARIA 角色。写 <code><div class="nav">,它就真当那是普通容器,连“导航”两个字都不会提——用户听到的只是“首页”,不知道这是菜单,也无法用 N 键跳入。
<p>必须用 <code><nav></nav>,它自带 role="navigation",且主流读屏器(NVDA/VoiceOver/JAWS)都把它当作可跳转地标。多个 <nav></nav> 时,得加 aria-label 区分,否则全叫“导航”,用户根本分不清主导航、页脚导航还是面包屑。
<main></main> 缺失会导致正文定位失败
<main></main> 缺失会导致正文定位失败几乎所有屏幕阅读器把 <main></main> 当作“页面主体内容”的唯一可信锚点。没它,系统会退而求其次找 role="main",再找不到就从第一个 <h1></h1> 开始算正文——但页眉里也可能有 <h1></h1>,结果用户下拉 7–12 次才摸到真正要读的内容。
硬约束必须遵守:<main></main> 一个页面只能有一个,不能嵌套在 <article></article>、<aside></aside>、<header></header>、<footer></footer> 或 <nav></nav> 里。React/Vue 项目里用 <div id="app"> 包住整个应用,却忘了在内部放 <code><main></main>,就是典型漏项。
<button></button> 和 role="button" 的键盘交互差距
二者在读屏器里可能都读成“按钮”,但行为天差地别:<button></button> 自动支持空格/回车触发、disabled 状态禁用朗读与交互、默认焦点样式;<div role="button"> 必须手动补全:
<ul>
<li>
<code>tabindex="0"(否则键盘用户根本点不到)
onkeydown 监听空格和回车事件aria-disabled="true" + 对应逻辑(否则禁用后仍能触发):focus 样式(否则无视觉反馈)漏一项,键盘用户就会卡住。真实场景:Vue 里写 <div>提交</div>,测试只点鼠标,上线后视障用户完全无法提交。
哪些标签一换就能解决“读不出”问题
很多“屏幕阅读器读不出”本质是用了无语义容器替代结构化元素。直接替换效果立竿见影:
- 标题不用
<div class="title">,改用 <code><h1></h1>–<h6></h6>(层级必须连续,不能跳级) - 段落不用
<div>文字</div>,改用<p></p>(否则读屏器可能把几段连成一句) - 列表不用嵌套
<div> 模拟,必须用 <code><ul></ul>/<ol></ol>+<li>(否则无法告知用户“共 5 项”或“第 3 项”) - 纯图标按钮没文字?加
aria-label,别靠 CSS 隐藏文本(隐藏后读屏器也读不到)
最常被忽略的一点:<label></label> 必须显式关联表单控件,用 for + id 或包裹方式,否则读屏器不会把提示文本和输入框绑定,用户不知道该填什么。











