屏幕阅读器无法正确识别导航区域,若<nav>无链接或缺失aria-label,则读作“一组链接”而非“主导航”,且无法用N键跳转;<main>必须唯一且不嵌套,否则AT无法定位主内容;按钮须用<button>原生标签并设type="button",禁用状态需用disabled属性;标题层级须连续,H1不可缺失或重复,避免语义与视觉脱节。

屏幕阅读器能否正确识别导航区域
如果 <nav> 没有包裹可操作链接,或缺失 aria-label,NVDA、VoiceOver 等工具会把它读作“一组链接”,而非“主导航”。用户无法用快捷键(如 N 键)直接跳转。
常见错误包括:
- 用
<div class="nav">替代<nav> -
<nav>内部只有文字、图标,没有<a>或<button> - 多个
<nav>未区分用途,比如没给侧边栏导航加aria-label="侧边栏导航"
修复建议:每个 <nav> 必须含至少一个可聚焦元素,并通过 aria-label 或 aria-labelledby 明确其功能。
main 元素是否唯一且位置合理
<main> 是屏幕阅读器“跳过导航”的默认锚点。若页面存在多个 <main>,或它被嵌套在 <article> / <section> 内部,AT(辅助技术)将无法准确定位主内容区。
立即学习“前端免费学习笔记(深入)”;
典型问题场景:
- 服务端渲染模板中,
<main>被重复注入(如 header 组件和 page 组件各自输出一个) - SPA 应用中,路由切换时未清空旧
<main>,导致 DOM 中残留多个 -
<main>包裹了页脚或广告位等非核心内容
验证方式:在 Chrome DevTools 的 Accessibility 面板中搜索 main,确认只出现一次,且其 role="main" 属性未被覆盖。
按钮与链接是否具备原生语义和键盘支持
用 <div onclick="..."> 或 <span tabindex="0"> 模拟按钮,即使加了 aria-role="button",仍可能缺失空格/回车触发、焦点管理、禁用状态样式同步等问题。
关键判断点:
- 是否用了
<button>?——优先选它,无需额外绑定事件 - 是否设置了
type="button"?——避免表单意外提交 - 是否用
<a href="#">代替<button>?——这是反模式,href="#" 会触发页面滚动且无语义 - 禁用状态是否同时用了
disabled属性和 CSSpointer-events: none?——后者会阻断 AT 识别
真实案例:某电商商品卡片的“加入购物车”用 <div class="btn"> 实现,键盘用户 Tab 到该元素后按空格无响应,必须改用 <button type="button"> 才能修复。
标题层级是否连续且反映真实内容结构
屏幕阅读器用户常靠 H1–H6 快速扫描页面逻辑。跳过一级标题(如从 <h1> 直接到 <h3>),或用 CSS 隐藏 <h2> 却不移除语义,都会破坏上下文感知。
容易忽略的细节:
-
<h1>缺失或重复(如每个组件都渲染一个<h1>) - 用
<h2>做副标题但实际是视觉装饰,应改用<span>+ CSS 样式 - 动态渲染的标题(如 React 中 useEffect 设置
document.title)未同步更新 DOM 中的<h1> - 多语言站点中,
<h1>文本与<title>不一致,导致 AT 用户困惑
检查方法:打开 axe DevTools,运行“Heading levels”规则;或在 VoiceOver 中按 H 键遍历所有标题,听是否连贯、无跳跃。
HTML 代码质量的无障碍维度,不取决于你写了多少 ARIA 属性,而在于你有没有让原生标签各司其职。最常被绕过的其实是 <main> 的唯一性、<button> 的默认行为、以及标题的实际层级——这些地方一出错,辅助技术就失去上下文,再漂亮的 ARIA 也补不回来。



















