跟着 MDN 学无障碍 Day 7:WAI-ARIA 基础
引言
随着 Web 应用日益复杂化和动态化,开发者面临的不仅是功能实现上的挑战,还有日益严峻的无障碍问题。传统的 HTML 语义元素在处理静态内容时表现出色,但当涉及复杂的 UI 控件、动态内容更新以及非标准交互时,其表达能力就显得捉襟见肘。WAI-ARIA 正是为解决这些问题而诞生的技术规范,它通过一组额外的 HTML 属性来弥补语义的缺失,让辅助技术能够正确理解并传达页面内容。
什么是 WAI-ARIA
背景与诞生
在 HTML5 提供nav、footer、aside等语义标签之前,开发者通常使用带有id或class的div来构建页面结构。例如主导航通常写作<div class="nav">。这种做法对于视力正常的用户来说没有问题,但对于依赖屏幕阅读器的用户而言,这些div无法传达任何结构信息,用户无法快速定位到主导航或侧边栏等重要区域。
早期的解决办法是在页面顶部放置隐藏的跳转链接,例如:
<ahref="#hidden"class="hidden">Skip to navigation</a>这种方法虽然在一定程度上提供了帮助,但使用场景有限,且在不同辅助技术中的表现并不一致。
另一个典型的场景涉及复杂的表单控件,如日期选择器或范围滑块。HTML5 提供了对应的input类型,但跨浏览器兼容性差且样式难以定制,因此开发者往往借助 JavaScript 库来实现这些功能。这些库通常使用大量嵌套的div构建界面,视觉上功能完整,但屏幕阅读器只能看到一堆结构混乱且毫无语义的元素。
WAI-ARIA 的核心概念
WAI-ARIA 是 W3C 制定的一套规范,定义了可以在其他元素上使用的 HTML 属性,用以补充缺失的语义信息。规范主要包含三大类特性:
角色用于定义元素的功能。许多标志性角色与 HTML5 结构元素的语义相对应,例如role="navigation"对应nav元素,role="complementary"对应aside元素。此外还有描述更具体 UI 结构的角色,如role="tablist"、role="tab"、role="search"等。
属性为元素赋予额外的含义或语义信息。例如aria-required="true"表示表单字段必须填写,aria-labelledby允许引用页面中的其他元素作为标签,这弥补了label元素只能关联单个输入框的局限。
状态用于表达元素当前的条件,与属性不同的是状态会随着应用的生命周期发生变化。例如aria-disabled="true"表示表单输入处于禁用状态,开发者可以通过 JavaScript 动态更新这一状态。
WAI-ARIA 的重要特征在于它不会对页面的视觉呈现和 DOM 结构产生任何影响。它的作用仅限于通过浏览器向无障碍 API 暴露更多信息,这些信息最终被屏幕阅读器等辅助技术所利用。开发者也可以利用这些 ARIA 属性作为 CSS 选择器进行样式定制。
何时使用 WAI-ARIA
使用 WAI-ARIA 应遵循一个核心原则:仅在必要时使用。理想情况下,开发者应当优先使用原生 HTML 元素来实现语义表达,因为原生元素自带键盘交互和无障碍支持,无需额外工作。但现实情况下存在两种必须借助 ARIA 的场景:一是对代码的控制权有限,无法修改底层 HTML 结构;二是需要实现原生 HTML 无法支持的复杂功能。
WAI-ARIA 主要解决了四个核心领域的问题:
- 路标与地标:ARIA 的角色属性可以为页面不同功能区域提供语义标识,帮助屏幕阅读器用户快速导航。
- 动态内容更新:
aria-live属性可以通知屏幕阅读器关注特定区域的内容变化。 - 键盘无障碍操作:通过
tabindex属性的扩展值,开发者可以将原本无法聚焦的元素纳入键盘导航序列。 - 非语义控件:当开发者必须使用
div等非语义元素构建复杂 UI 时,ARIA 可以为其补充缺失的语义信息。
路标与地标的实现
基础结构分析
以一份典型的页面结构为例,包含页眉、导航、主内容、侧边栏和页脚。使用 HTML5 语义标签编写如下:
<header><h1>页面标题</h1><nav><ul><li><ahref="#">首页</a></li><li><ahref="#">关于</a></li></ul><form><inputtype="text"placeholder="搜索"/></form></nav></header><main><article>文章内容</article><aside>侧边栏信息</aside></main><footer>页脚信息</footer>在支持 HTML5 的浏览器中,屏幕阅读器能够识别这些语义元素并告知用户。例如 VoiceOver 会报告 banner、navigation、main、complementary 和 footer 等地标。
使用 ARIA 增强地标
尽管 HTML5 语义标签已能提供基本的地标信息,但仍有优化空间。搜索表单是用户经常需要快速定位的功能,可以为表单添加role="search"来明确其用途:
<header><h1>页面标题</h1><navrole="navigation"><ul><li><ahref="#">首页</a></li><li><ahref="#">关于</a></li></ul><formrole="search"><inputtype="search"name="q"placeholder="搜索关键词"aria-label="搜索整个站点内容"/></form></nav></header><main><articlerole="article">文章内容</article><asiderole="complementary">侧边栏信息</aside></main><footer>页脚信息</footer>在这个优化版本中,search角色使搜索表单在地标菜单中成为独立项目,便于快速访问。同时,input元素上的aria-label属性为没有可见标签的输入框提供了描述性名称,屏幕阅读器会将其朗读给用户。
对于仍在使用 IE8 等老旧浏览器的用户,这些 ARIA 角色的价值更为突出。如果页面完全使用div构建,ARIA 角色几乎是提供必要语义的唯一手段。
动态内容更新的通知
实时区域的问题
现代 Web 应用中频繁出现动态内容更新,例如实时聊天、股票行情、购物车状态变化等。这些内容变化虽然视觉上可见,但默认情况下屏幕阅读器无法感知。用户可能完全不知道页面中的某个区域已经更新了内容。
以下是一个简单的随机名言展示示例,页面每隔十秒更新一次引用的内容:
<section><h1>随机名言</h1><blockquote><p></p></blockquote></section>JavaScript 代码通过fetch加载数据,并使用setInterval定时更新内容:
constintervalID=setInterval(showQuote,10000);功能本身没有问题,但屏幕阅读器用户无法获知内容正在发生变化。
aria-live 的解决方案
aria-live属性可以将元素标记为实时区域,当内容发生变化时,屏幕阅读器会朗读更新内容。该属性有三个可选值:
off:默认值,不进行任何通知。polite:在用户空闲时进行通知,不会打断当前操作。assertive:尽快通知用户,可能会轻微打断当前操作。rude:立即通知,可能严重打断用户操作,应谨慎使用。
对于大多数场景,assertive级别的通知已经足够。将实时区域标记如下:
<sectionaria-live="assertive"><h1>随机名言</h1><blockquote><p></p></blockquote></section>aria-atomic 确保完整朗读
默认情况下,屏幕阅读器只朗读实际发生变化的部分文本。如果希望每次更新时都朗读整个内容区域,可以使用aria-atomic属性:
<sectionaria-live="assertive"aria-atomic="true"><h1>随机名言</h1><blockquote><p></p></blockquote></section>设置为true后,屏幕阅读器会将整个section视为一个不可分割的单元,每次更新都会完整朗读标题和内容。
键盘无障碍的优化
tabindex 的扩展用法
HTML 中可聚焦的元素默认包括链接、按钮和表单控件。开发者可以通过键盘的 Tab 键在这些元素之间顺序导航。但对于使用div或span构建的自定义控件,默认是不可聚焦的,键盘用户无法访问。
WAI-ARIA 扩展了tabindex属性的用法,提供了两个关键值:
tabindex="0":使元素成为可 Tab 聚焦的,它会按照 DOM 顺序出现在 Tab 键的导航序列中。这是最常用的值。tabindex="-1":允许元素通过 JavaScript 编程方式获得焦点,例如在点击事件中调用focus方法,但元素本身不会出现在 Tab 键的导航序列中。
实践示例
将一个div改造为可交互的按钮时,需要同时添加tabindex和键盘事件处理:
<divdata-message="第一条消息"tabindex="0"role="button">点击我</div>对应的 JavaScript 需要处理键盘事件,确保按下空格键或回车键时触发与鼠标点击相同的操作:
constbutton=document.querySelector('div[role="button"]');button.addEventListener('click',handleClick);button.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();handleClick.call(this,e);}});这种做法使得自定义控件既可通过鼠标操作,也可通过键盘操作,实现了设备无关的交互。
非语义控件的语义补充
表单验证中的 ARIA
表单验证是无障碍实践中的重要环节。在错误信息的展示中,可以使用role="alert"将错误容器标记为告警区域:
<divclass="errors"role="alert"aria-relevant="all"><ul></ul></div>role="alert"自动将元素转换为实时区域,当错误信息出现时,屏幕阅读器会立即朗读。aria-relevant="all"则指示屏幕阅读器在错误列表发生任何变化时都进行通知,包括添加和删除。
对于必填字段,可以使用aria-required属性进行标记:
<inputtype="text"name="name"id="name"aria-required="true"/><inputtype="number"name="age"id="age"aria-required="true"placeholder="请输入 1 到 150 之间的数字"/>屏幕阅读器在聚焦这些输入框时会朗读 required 提示,让用户了解该字段必须填写。
描述非语义按钮
当开发者不得不使用div构建按钮时,仅添加tabindex无法解决语义问题。屏幕阅读器仍然无法识别这个元素是一个按钮:
<divdata-message="这是第一个按钮"tabindex="0">点击我</div>屏幕阅读器可能会将其朗读为group或其他不明确的角色。添加role="button"可以解决这一问题:
<divdata-message="这是第一个按钮"tabindex="0"role="button">点击我</div>此时屏幕阅读器会正确地将该元素识别为button,并向用户传达其功能。即便如此,开发者仍应优先使用原生的button元素,因为它提供了所有内置的无障碍支持,包括键盘交互、焦点管理和语义信息。
复杂小部件的 ARIA 实现
选项卡界面的 ARIA 改造
以选项卡界面为例,原始实现使用链接列表作为选项卡触发器,内容面板则是普通div。这种结构在功能上可以工作,但屏幕阅读器无法理解选项卡之间的关系。
ARIA 优化的版本使用了以下结构和属性:
<ulrole="tablist"><liclass="active"role="tab"aria-selected="true"aria-setsize="3"aria-posinset="1"tabindex="0">标签一</li><lirole="tab"aria-selected="false"aria-setsize="3"aria-posinset="2"tabindex="0">标签二</li><lirole="tab"aria-selected="false"aria-setsize="3"aria-posinset="3"tabindex="0">标签三</li></ul><divclass="panels"><articleclass="active-panel"role="tabpanel"aria-hidden="false">标签一的内容</article><articlerole="tabpanel"aria-hidden="true">标签二的内容</article><articlerole="tabpanel"aria-hidden="true">标签三的内容</article></div>role="tablist"标记了选项卡容器的角色。role="tab"和role="tabpanel"分别定义了选项卡和内容面板。aria-selected指示当前选中的选项卡。aria-hidden控制内容面板是否对辅助技术可见。aria-setsize和aria-posinset共同告知屏幕阅读器当前元素在系列中的位置,例如 1 of 3 或 2 of 3。
这些属性使得屏幕阅读器能够正确识别选项卡结构,并告知用户当前所在的选项卡编号和总数。
状态管理的最佳实践
在选项卡切换时,需要同步更新多个 ARIA 状态。被选中的选项卡应设置aria-selected="true",对应的内容面板应设置aria-hidden="false"。未选中的选项卡和面板则应设置为相反的值。
JavaScript 负责在用户切换时更新这些状态属性,同时更新对应的 CSS 类来控制视觉样式。这种做法确保了视觉呈现和无障碍语义始终保持一致。
总结
WAI-ARIA 为 Web 无障碍提供了一套强大且灵活的语义补充机制。通过角色、属性和状态三大核心概念,它弥合了复杂 UI 与辅助技术之间的沟通鸿沟。
在路标与地标方面,ARIA 角色帮助屏幕阅读器用户快速定位页面中的关键功能区域。在动态内容方面,aria-live确保用户能够感知到内容更新。在键盘操作方面,tabindex扩展值让自定义控件进入键盘导航序列。在非语义控件方面,role和aria-*属性为div构建的界面补充了缺失的语义。
使用 WAI-ARIA 应遵循"仅在必要时使用"的原则。优先使用原生 HTML 元素,仅在无法实现或控制权受限时才借助 ARIA。同时,建议让真实的用户参与测试,包括使用屏幕阅读器和键盘导航的用户,他们的反馈往往能发现开发者难以察觉的问题。
