C01 无障碍与 ARIA
摘要:无障碍不是额外功能,而是页面在「不用鼠标、不用屏幕」时的可用性下限。浏览器把 DOM 转成可访问性树,朗读软件与键盘导航都读这棵树;标签选错时这棵树会残缺,而 ARIA 只能补充语义,不能修复错误的标签。本文给出五条 ARIA 使用原则、一个可键盘走通的页面骨架,以及三项验证手段。
关键词:无障碍,可访问性树,ARIA,地标,焦点
所属:frontend/html / C-高级 | 上一篇:C00-总结 | 下一篇:C02-表单校验进阶
1 引言
页面在三种受限条件下会暴露问题:只用键盘、使用朗读软件、关闭动画与颜色。三者共同依赖同一棵树——浏览器根据 DOM 生成的可访问性树。
本文说明这棵树的来源、ARIA 的正确用法与验证方式。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 语义 | 地标、标题、替代文本、表格关联 | 完整 WCAG 条款逐条对照 |
| ARIA | role、aria-label、aria-labelledby、aria-live、aria-expanded | 复杂组件的状态机设计 |
| 键盘 | 焦点顺序、可见焦点、跳过链接 | 自定义组件的焦点陷阱实现 |
| 验证 | 无障碍面板、键盘遍历、朗读软件 | 自动化审计工具的规则库 |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | A02 的本地服务器 | 能访问 localhost |
| 文件 | html-practice/a11y.html |
编码 UTF-8 |
| 工具 | Chrome 开发者工具无障碍面板(Elements → Accessibility) | 能看到可访问性树 |
| 前置 | A08、A07 | 语义骨架与表单可写 |
3 原理与实现
3.1 可访问性树的来源
浏览器把 DOM 中的语义换算成角色、名称与状态,辅助技术只读这份结果,如图 3-1 所示。
flowchart LR
A["HTML 标签"] --> B["DOM 树"]
B --> C["可访问性树<br/>角色 / 名称 / 状态"]
C --> D["朗读软件<br/>按角色播报"]
C --> E["键盘导航<br/>按可聚焦元素移动"]
C --> F["浏览器阅读模式<br/>按地标提取正文"]
图 3-1 DOM 到可访问性树的换算
图上可见:角色由标签决定,名称由内容或标签关联补齐;
div在这棵树里既无角色也无名称,等同于不存在。
3.2 可键盘走通的骨架
<!-- 运行环境:Chrome 130+;body 内内容 -->
<a class="skip" href="#main">跳到正文</a>
<header>
<nav aria-label="主导航">
<ul>
<li><a href="./index.html" aria-current="page">首页</a></li>
<li><a href="#faq">常见问题</a></li>
</ul>
</nav>
</header>
<main id="main" tabindex="-1">
<h1>无障碍检查清单</h1>
<section id="faq">
<h2>常见问题</h2>
<details>
<summary aria-expanded="false">为什么不能只靠颜色区分状态?</summary>
<p>色觉差异人群无法分辨红绿,需要同时给出文字或图标。</p>
</details>
<p aria-live="polite" id="status">尚未提交。</p>
</section>
</main>
<style>
/* 焦点必须可见 */
:focus-visible { outline: 2px solid #0f172a; outline-offset: 2px; }
.skip { position: absolute; left: -999px; }
.skip:focus { left: 1rem; top: 1rem; }
</style>
ARIA 的使用原则见表 3-1。
| 原则 | 说明 | 反例 |
|---|---|---|
| 能用原生标签就不用 ARIA | 原生标签自带角色、键盘与状态 | 用 div role="button" 代替 button |
| 不覆盖原生语义 | 给 button 加 role="link" 会造成混乱 |
同一元素两种角色 |
| ARIA 只补不可见的语义 | 如无文字按钮的名称、动态区域的通知 | 用 ARIA 表达视觉样式 |
| 名称必须存在 | 交互元素的名称来自文字、标签或 aria-label |
图标按钮无名称 |
| 状态必须随交互更新 | 展开、选中、当前页需要同步属性 | 面板展开了 aria-expanded 仍是 false |
常用属性与用途见表 3-2。
| 属性 | 用途 | 使用场景 |
|---|---|---|
aria-label |
给元素一个名称 | 无文字的图标按钮 |
aria-labelledby |
引用页面上已有文字作为名称 | 弹层标题 |
aria-describedby |
补充说明 | 输入框的错误提示 |
aria-live |
动态区域播报(polite/assertive) |
提交结果、加载状态 |
aria-expanded |
展开状态 | 折叠面板、菜单 |
aria-current |
当前项 | 导航中的当前页 |
aria-hidden="true" |
从可访问性树中移除 | 纯装饰图标 |
4 验证与讨论
- 保存文件,刷新后只按
Tab键遍历:第一个焦点应落在「跳到正文」,回车后焦点进入正文。 - 打开 Elements → Accessibility 面板,逐项核对导航、正文、按钮的角色与名称是否存在。
- 开启系统朗读功能,听取标题、导航与折叠面板的播报内容。
- 用取色工具把页面转为灰度,确认状态区分不依赖颜色。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 跳过链接 | Tab 首次聚焦可见,回车跳到正文 |
键盘用户可绕过导航 |
| 焦点可见 | 每个可聚焦元素都有明显描边 | :focus-visible |
| 地标 | 面板显示 header / nav / main 角色 | 语义标签生效 |
| 图标按钮名称 | 面板显示按钮名称 | aria-label |
| 动态播报 | 提交后朗读「已提交」 | aria-live |
讨论与局限。 ARIA 只能补语义,无法修复错误的交互实现;给 div 加 role="button" 后仍需自行处理回车与空格键。另 aria-live 区域初始必须是空的,先写内容再更新才可能被播报。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 键盘无法到达某按钮 | 用 div 加点击事件,不可聚焦 |
改用 button,或补 tabindex="0" 与按键处理 |
| 朗读软件念出「按钮 按钮」 | 角色重复声明 | 删除多余的 role |
| 弹层打开后朗读仍读背景 | 未设置 aria-hidden 或未用遮罩 |
背景加 aria-hidden="true" |
| 状态变化无播报 | 动态区域初始已有内容 | 改为先清空再写入 |
| 跳过链接不可见 | 只做了位移隐藏,未处理聚焦样式 | 补 :focus 下的可见样式 |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 辅助技术读可访问性树,标签决定角色与名称 | 图 3-1 |
| 2 | 原生标签优先,ARIA 只补不可见语义 | 表 3-1 |
| 3 | 交互元素必须有名称,状态必须同步 | 表 3-1 |
| 4 | 只用键盘走一遍是最低验证门槛 | 4 节步骤 1 |
- 上一篇:C00-总结
- 下一篇:C02-表单校验进阶
- 返回:C00-总结 | 科目 README
参考文献
[1] WCAG 2.2 快速参考,W3C,https://www.w3.org/WAI/WCAG22/quickref/(访问于 2026-10-29)
[2] MDN Web Docs · ARIA,https://developer.mozilla.org/zh-CN/docs/Web/Accessibility/ARIA(访问于 2026-10-29)

评论 0
还没有评论,快来抢沙发吧~