理解 HTML 中的无障碍角色
无障碍角色告诉辅助技术一个元素是什么,而不是它看起来像什么。当屏幕阅读器遇到一个按钮时,它需要知道这是一个按钮——无论是原生的 <button> 元素还是带有 role="button" 的自定义组件。如果这一点做错了,意味着数百万用户无法有效地浏览你的界面。
本指南解释了无障碍角色如何融入更广泛的无障碍树中,何时使用 ARIA 角色与语义化 HTML,以及保持代码既可访问又易于维护的最佳实践。
核心要点
- 无障碍角色为辅助技术定义元素是什么,而不是其视觉外观
- 无障碍树包含每个元素的名称、角色和值/状态
- 语义化 HTML 提供隐式角色——仅在 HTML 不足时使用 ARIA
- 使用真实的辅助技术进行测试可确保你的实现对实际用户有效
角色在无障碍树中的工作原理
每个网页都有两棵树:你熟悉的 DOM 树,以及辅助技术实际读取的无障碍树。无障碍树遵循一个简单的模型:每个元素都有一个名称、角色和值/状态。
<!-- DOM 元素 -->
<button aria-pressed="true">静音</button>
<!-- 无障碍树表示 -->
名称: "静音"
角色: button
值/状态: pressed=true
浏览器会自动从你的 HTML 构建这个无障碍树,将语义化元素映射到它们的隐式角色。<nav> 元素变成导航地标,<button> 变成按钮小部件。ARIA 角色允许你在原生 HTML 不够用时修改这棵树——但它们应该是你的最后手段,而不是首选。
四大主要角色类别
地标角色
地标角色定义用于导航的页面区域。现代 HTML5 原生提供了其中大部分:
<header>→ banner 角色(当不在<article>或<section>内时)<nav>→ navigation 角色<main>→ main 角色<aside>→ complementary 角色<footer>→ contentinfo 角色(当不在<article>或<section>内时)
最佳实践:使用语义化 HTML 元素而不是 <div role="navigation">。<nav role="navigation"> 的冗余没有增加任何价值——该元素已经隐式具有该角色。
小部件角色
小部件角色描述交互控件。这些对于无法使用原生元素的自定义组件至关重要:
<!-- 好的做法:自定义选项卡界面 -->
<div role="tablist">
<button role="tab" aria-selected="true">设置</button>
<button role="tab" aria-selected="false">个人资料</button>
</div>
<!-- 不好的做法:不必要的 ARIA -->
<button role="button">点击我</button> <!-- 冗余 -->
文档结构角色
这些角色描述内容组织:标题、列表、文章和分隔符。同样,优先使用语义化 HTML:
<!-- 推荐这样 -->
<article>
<h2>文章标题</h2>
</article>
<!-- 而不是这样 -->
<div role="article">
<div role="heading" aria-level="2">文章标题</div>
</div>
抽象角色
抽象角色如 command 或 composite 是 WAI-ARIA 中的基础模板。永远不要直接使用这些——它们仅存在于规范中用于定义其他角色。
Discover how at OpenReplay.com.
ARIA 角色的现代最佳实践
优先使用语义化 HTML
ARIA 的第一条规则是不要使用 ARIA。原生 HTML 元素自带键盘支持、焦点管理和无障碍语义:
<!-- 始终优先使用这个 -->
<button onclick="submit()">提交</button>
<!-- 而不是这个反模式 -->
<div role="button" tabindex="0" onclick="submit()">提交</div>
<div> 版本需要额外的 JavaScript 来支持键盘操作、焦点样式和正确的激活——而原生按钮会自动完成这些工作。
避免冗余的地标角色
自 HTML5 以来,向语义化元素添加显式角色是不必要的,可能会造成混淆:
<!-- 不要这样做 -->
<main role="main">
<nav role="navigation">
<header role="banner">
<!-- 这些元素已经具有隐式角色 -->
<main>
<nav>
<header>
永远不要覆盖原生交互角色
更改按钮的角色会破坏其预期行为:
<!-- 永远不要这样做 -->
<button role="heading">章节标题</button>
<!-- 这会破坏键盘交互和屏幕阅读器的预期 -->
何时使用自定义角色才合理
自定义组件有时确实需要 ARIA 角色。以下是有效的使用场景:
自定义对话框模式
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">确认操作</h2>
<button>取消</button>
<button>确认</button>
</div>
自定义选项卡界面
<div role="tablist" aria-label="用户设置">
<button role="tab" aria-selected="true" aria-controls="panel-1">常规</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">隐私</button>
</div>
<div role="tabpanel" id="panel-1">常规设置内容</div>
处理描述和标签
对于无障碍名称和描述,遵循以下层次结构:
- 可见文本(对所有用户最好)
aria-labelledby(引用其他地方的可见文本)aria-describedby(添加补充信息)aria-label(当可见文本不可行时)
<button aria-describedby="help-text">删除</button>
<span id="help-text">此操作无法撤销</span>
注意:aria-description 正在 WAI-ARIA 1.3 中出现,但支持有限。在生产代码中坚持使用 aria-describedby。
要避免的常见反模式
基于 div 的按钮:当 <button> 完全可用时创建 <div role="button">
角色堆砌:“以防万一”地向每个元素添加角色
冲突的语义:<h2 role="button"> 混合了结构和交互
冗余角色:<nav role="navigation"> 没有增加任何价值
测试你的实现
始终使用实际的辅助技术验证角色。浏览器开发者工具现在包含无障碍树检查器——使用它们来准确查看屏幕阅读器感知到的内容。在 Windows 上使用 NVDA 或在 macOS 上使用 VoiceOver 进行测试,以确保你的角色转化为可用的体验。
结论
无障碍角色的存在是为了描述某物是什么,而不是它看起来如何。原生语义化 HTML 隐式提供了大多数角色——首先使用它。将 ARIA 角色保留给 HTML 确实不足的真正自定义组件。当你确实使用 ARIA 时,确保它符合 WCAG 指南并使用真实的辅助技术进行测试。记住:没有 ARIA 比糟糕的 ARIA 更好。
常见问题
不可以,每个元素只能有一个 role 属性。如果你需要多个语义含义,考虑重构你的 HTML 以使用嵌套元素,或选择最能代表元素主要目的的单一角色。
绝对不需要。大多数 HTML 元素都有隐式角色,无需 ARIA 即可完美工作。只有在创建自定义小部件或语义化 HTML 无法表达所需功能时才添加角色。过度使用 ARIA 往往会造成比解决更多的无障碍问题。
ARIA 总是优先于原生 HTML 语义,这可能会破坏预期行为。例如,向按钮添加 role='heading' 会移除屏幕阅读器的所有按钮功能。这就是为什么你永远不应该覆盖原生交互元素角色。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.