12k
All articles

理解 HTML 中的无障碍角色

HTML 中的无障碍角色可告知辅助技术各元素的语义,涵盖 ARIA 角色、语义化 HTML 的应用,以及借助 NVDA 或 VoiceOver 进行测试的方法。

OpenReplay Team
OpenReplay Team
理解 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>

抽象角色

抽象角色如 commandcomposite 是 WAI-ARIA 中的基础模板。永远不要直接使用这些——它们仅存在于规范中用于定义其他角色。

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>

处理描述和标签

对于无障碍名称和描述,遵循以下层次结构:

  1. 可见文本(对所有用户最好)
  2. aria-labelledby(引用其他地方的可见文本)
  3. aria-describedby(添加补充信息)
  4. 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 更好。

常见问题

我可以在单个元素上使用多个 ARIA 角色吗?

不可以,每个元素只能有一个 role 属性。如果你需要多个语义含义,考虑重构你的 HTML 以使用嵌套元素,或选择最能代表元素主要目的的单一角色。

我需要向所有 HTML 元素添加 ARIA 角色吗?

绝对不需要。大多数 HTML 元素都有隐式角色,无需 ARIA 即可完美工作。只有在创建自定义小部件或语义化 HTML 无法表达所需功能时才添加角色。过度使用 ARIA 往往会造成比解决更多的无障碍问题。

如果我使用冲突的 ARIA 属性和 HTML 语义会发生什么?

ARIA 总是优先于原生 HTML 语义,这可能会破坏预期行为。例如,向按钮添加 role='heading' 会移除屏幕阅读器的所有按钮功能。这就是为什么你永远不应该覆盖原生交互元素角色。

Open-source session replay

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.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.