ARIA:毒药还是解药?

Aaron Leventhal
Aaron Leventhal

借助 ARIA,Web 作者可以创建一个仅供屏幕阅读器看到的替代现实。

有时,有必要向屏幕阅读器扩展真相甚至纯粹的“谎言”内容,以了解网页内容中发生的情况。例如,“焦点就在这里!”或“这真的是一个滑块!”。这就像在工作台上的工具和微件上添加神奇的便笺一样。这些神奇的便笺让每个人都相信上面写的内容。

当有神奇的便利贴时,它会覆盖我们对每种工具的认知或用途。例如,如果您添加了 ARIA 来声明“这里的东西是胶枪!”虽然是一个空的蓝色盒子,但神奇的便笺告诉我们这是一把胶水枪。还可以添加“并且满了 30%!”。屏幕阅读器现在会报告胶枪已用完 30%。

在 Web 上,相当于使用一个包含图片的普通 div,并使用 ARIA 指明它是一个滑块,值为 30(满分 100)。ARIA 不会将 div 转换为滑块;它只会指示屏幕阅读器将其读出为滑块。

ARIA 不会影响网页的外观,也不会影响使用鼠标或键盘的用户的行为。 只有使用辅助技术的用户会注意到 ARIA 的影响。Web 开发者可以添加任何任意的 ARIA,而不会影响未运行辅助技术的用户。

没错,ARIA 实际上不会对键盘焦点或标签页顺序执行任何操作。所有这些都是在 HTML 中完成的,有时还会使用一些 JavaScript 进行调整。

ARIA 如何运作?

屏幕阅读器或其他辅助技术会向浏览器询问每个元素的相关信息。当元素上存在 ARIA 时,浏览器会获取相关信息,并更改向屏幕阅读器提供的有关该元素的信息。

以下是 ARIA 的一些常见用法。

  • 添加 HTML 中不存在的特殊组件,例如自动补全、树或电子表格。
  • 添加 HTML 中存在的组件,但作者决定重新进行构建,这可能是因为他们想要更改标准元素的行为或外观。例如,HTML <input type="range"> 元素本质上就是一个滑块,但作者希望使其看起来有所不同。
    • 在大多数情况下,可以使用 CSS 解决此问题。不过,对于 range,CSS 很不方便。作者可以自行制作滑块,并将 role="slider"aria-valuenow 搭配使用,以告知键盘当前值。
  • 添加实时区域,以告知屏幕阅读器页面某个区域的相关更改。
  • 创建地标,例如标题。地标有助于屏幕阅读器用户更快地找到所需内容。地标可以包含整个相关区域。例如,“此容器是网页的主要区域”和“此处的容器是导航面板”。

为何使用 ARIA?

向已经正常运行的 HTML 添加一些 ARIA 可能很有用。例如,我们可能希望表单控件指向针对无效输入的错误消息提醒。或者,我们可能希望指明文本输入的具体用途。这些调整可以让普通网站更易于通过屏幕阅读器使用。

假设本地网站商店不销售我们需要的所有 widget。不过,我们是 MacGyver。我们可以根据其他微件来发明自己的微件!这与需要制作菜单栏的网站作者非常相似。

虽然 <nav> 元素存在,但菜单栏通常是使用 div、图片、按钮、点击处理脚本、按键处理脚本和 ARIA 拼凑而成的。

支持鼠标用户

我们来一起制作一个菜单栏。我们在名为 div 的通用框元素中显示了一系列项。每次用户点击 div 时,它都会执行相应的命令。太棒了,对使用鼠标点击器的用户来说很实用!

接下来,我们将其美化。我们使用 CSS 将元素整齐地排列,并在元素周围添加视觉轮廓。我们让它看起来足够像其他菜单栏,让观众直观地知道它是一个菜单栏及其使用方法。我们的菜单栏甚至会针对鼠标悬停的任何项使用不同的背景颜色,为用户提供一些有用的视觉反馈。

某些菜单项是父级菜单项。它们会生成子子菜单。每当用户将鼠标悬停在其中一个图标上时,我们都会启动一个动画,以滑出子子菜单。

与网络上的许多内容一样,这些内容非常难以访问。

使菜单栏键盘便于访问

我们来添加键盘无障碍功能。这只需要更改 HTML,不需要 ARIA。请注意,对于不使用辅助技术的用户,ARIA 不会影响外观、鼠标或键盘等核心方面。

就像网页可以响应鼠标一样,它也可以响应键盘。我们的 JavaScript 可以监听发生的所有按键操作,并确定按键操作是否有用。如果不符合条件,则会将其抛回页面,就像鱼太小不能吃一样。我们的规则如下:

  • 如果用户按下箭头键,我们来看看自己的内部菜单栏蓝图,并确定新的活动菜单项应该是什么。我们会清除所有当前的突出显示,并突出显示新菜单项,以便视力正常的用户直观地知道自己所在的位置。然后,网页应调用 event.preventDefault() 以阻止浏览器执行常规操作(在本例中为滚动网页)。
  • 如果用户按下 Enter 键,我们可以将其视为点击,并执行相应的操作(甚至打开其他菜单)。
  • 如果用户按了应执行其他操作的键,请将其发送回页面。例如,我们的菜单栏不需要 Tab 键,因此请将其丢弃!这个问题很难解决。例如,菜单栏需要使用箭头键,但不能使用 Alt+ArrowCommand+Arrow。这些快捷键用于在浏览器标签页的网页历史记录中移动到上一个和下一个网页。如果作者不小心,菜单栏就会吞掉这些内容。这种 bug 经常发生,而我们还没有开始使用 ARIA!

屏幕阅读器可以访问我们的菜单栏

我们的菜单栏是用胶带和 div 创建的。因此,屏幕阅读器无法了解其中的任何内容。活动项的背景颜色只是一种颜色。菜单项 div 只是普通对象,没有特别的含义。因此,菜单栏的用户不会收到有关按哪些键或当前所选项的任何说明。

但这不公平!菜单栏对视力正常的用户起到了很好的作用。

使用 ARIA 进行救援。借助 ARIA,我们可以向屏幕阅读器假装焦点位于菜单栏中。如果作者的所有操作都正确无误,屏幕阅读器的自定义菜单栏就会像桌面应用中的菜单栏一样显示。

第一个 ARIA 谎言是 aria-activedescendant 属性。将该属性设置为活动菜单项的 ID,只要该值发生变化,就要小心进行更新。例如 aria-activedescendant="settings-menuitem"。这会导致屏幕阅读器将我们的 ARIA 活跃项视为焦点,并大声朗读或在盲文显示屏上显示该项。

“后代”一词是指某个项包含在另一个项的某个位置。相反的术语是“祖先”,即某个项被祖先包含。对于下一个向上/向下容器,这些容器可能会使用更具体的术语“父级/子级”。例如,假设某个文档中包含一个包含链接的段落。链接的父级是段落,但它也将文档作为祖先。 反之,文档也可能有多个段落子级,每个子级都带有链接。这些链接都是祖父级文档的后代。

通过使用 aria-activedescendant 从聚焦的菜单栏指向特定菜单项,屏幕阅读器现在知道用户移动到了什么位置,但不知道该对象的任何其他信息。这个 div 到底是什么?这时,角色属性就派上用场了。我们对整个内容的包含元素使用 role="menubar",然后对项组使用 role="menu",最后对各个菜单项使用 role="menuitem"

如果菜单项可以指向子菜单,该怎么办?用户需要知道这一点。 对于视力正常的用户,菜单的末尾可能会有一个三角形小图标,但屏幕阅读器至少目前还不支持自动读取图片。我们可以在每个可展开的菜单项上添加 aria-expanded="false",以指明存在可以展开的内容,但不能展开。此外,作者应在 img 三角形上放置 role="none",以指明其仅用于美化目的。这可以防止屏幕阅读器读出与图片有关的任何冗余信息。

修复了键盘 bug

虽然键盘访问是核心 HTML 的一部分,但很容易被覆盖。例如:

  • 复选框使用空格键进行切换,但作者忘记调用 preventDefault()。现在,空格键既会切换复选框,又会向下滚动页面,这是空格键的默认浏览器行为。
  • ARIA 模态对话框想要捕获其中的标签页导航。如果作者在对话框中忘记明确允许 Ctrl+Tab 打开新标签页,则它将无法按预期运行。
  • 作者创建一个选择列表并实现向上键和向下键。不过,作者仍需添加 Home、End、Pageup、pagedown 或首字母导航。

作者应遵循已知的模式。如需了解详情,请参阅资源部分。

对于纯键盘访问问题,在不使用屏幕阅读器或关闭虚拟浏览器模式的情况下尝试也会很有帮助。您可以在没有屏幕阅读器的情况下发现键盘错误,因为键盘访问是通过 HTML 而不是 ARIA 实现的。毕竟,ARIA 不会影响键盘或鼠标行为;而是向屏幕阅读器提供有关网页中的内容、当前焦点等的信息。

键盘 bug 几乎总是网页内容(尤其是 HTML 和 JavaScript)中的 bug,而不是 ARIA 中的 bug。

为什么有这么多?

作者在很多方面都会导致 ARIA 出错。每种错误都会导致完全崩溃或细微差异。细微的问题可能更糟糕,因为作者不太可能在发布前发现这些问题。

毕竟,除非作者是有经验的屏幕阅读器用户,否则 ARIA 中可能会出现问题。在我们的菜单栏示例中,作者可以认为,当“menuitem”正确时,应使用“option”角色。他们可能会忘记使用 aria-expanded、忘记在适当的时间设置和清除 aria-activedescendant,或者忘记创建包含其他菜单的菜单栏。菜单项数量如何?通常,屏幕阅读器会以“第 3 项(共 5 项)”等方式显示菜单项,以便用户知道自己所在的位置。浏览器通常会自动统计此值,但在某些情况下,以及在某些浏览器和屏幕阅读器组合中,计算出的数字可能会有误,作者需要使用 aria-posinsetaria-setsize 替换这些数字。

这只是菜单栏。想一想有多少种 widget。如果您愿意,可以浏览 ARIA 规范或创作做法。对于每种模式,ARIA 都可能以十几种方式被滥用。ARIA 依靠作者来了解自己在做什么。鉴于大多数作者都不是屏幕阅读器用户,可能会出现什么问题?

换言之,实际的屏幕阅读器用户必须在 ARIA 微件被认定为可交付之前完全试用这些微件。细节太多了。理想情况下,应使用多种不同的浏览器-屏幕阅读器组合来尝试所有内容,因为存在许多实现怪癖,以及一些未完成的实现。

摘要

ARIA 可用于替换或添加 HTML 中显示的任何内容。它可以对无障碍功能呈现方式进行细微更改,也可以构建整个体验。因此,对于不是屏幕阅读器用户的开发者来说,ARIA 既强大又危险。

ARIA 是一种标记层,会覆盖其他选项。当屏幕阅读器询问发生了什么时,如果存在 ARIA,用户就会获得 ARIA 版本的真实情况。

其他资源

W3C 的 ARIA 制作做法记录了每个示例的重要键盘导航特性,并提供了有效的 JavaScript、CSS 和 ARIA。这些示例侧重于目前可行的做法,不涵盖移动设备。

什么是无障碍功能 API?

无障碍 API 是屏幕阅读器或其他辅助技术了解网页中的内容和正在发生的情况的方式。例如 MSAA、IA2 和 UIA。无障碍功能 API 由以下两个部分组成:

  • 代表容器层次结构的对象“树”。例如,文档可以包含多个段落。段落可以包含文本、图片、链接和文本装饰。对象树中的每个项都可以具有属性,例如角色(我是什么?)、名称或标签、用户输入的值、说明以及布尔值状态,例如可聚焦、聚焦、必填、已选中。ARIA 可以替换其中任何属性。
    • 屏幕阅读器使用该树来帮助用户在虚拟缓冲区模式下导航,例如“请前往下一个标题”。
  • 描述树的变化的一系列事件,例如“焦点现在在这里!”。屏幕阅读器使用事件来告知用户刚刚发生了什么。当重要的 HTML 或 ARIA 标记发生更改时,系统会触发事件,以告知屏幕阅读器发生了更改。

HTML 可很好地映射到这些无障碍功能 API。如果 HTML 不够用,可以添加 ARIA,以便浏览器在将对象树或事件发送到屏幕阅读器之前替换 HTML 语义。