ARIA:毒药还是解药?

在屏幕阅读器中不加盐的话,这是如何纠正无障碍功能的!

亚伦·莱文萨尔
Aaron Leventhal

什么是 ARIA?

ARIA 让网页作者可以创造一种只能由屏幕阅读器读取的另类现实 🤥?

有时,有必要向屏幕阅读器扩展真实情况,甚至是完全“谎称”网络内容中发生的情况。例如,“焦点在这里!”或“这真的是一个滑块!”。这就像在工作台上的工具和微件上添加神奇的便笺一样。这些神奇的便笺可让所有人相信他们手上书写的内容。

只要存在神奇的便笺,它就会取代我们对每个工具或工具的某种认知。示例:“这里的这东西就是一把胶枪!”。尽管实际上它只是一个位于工作台上的空蓝色盒子,但神奇的便笺会让我们看到它是一把胶枪。我们还可以添加“它是 30% 的!”。屏幕阅读器现在会报告有 30% 的胶水枪。

与此相对应的 Web 内容是获取一个内含图片的普通框元素 (div),然后使用 ARIA 将其设为一个值为 30/100 的滑块。

哪些不是 ARIA?

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

没错:ARIA 对键盘焦点或 Tab 键顺序没有任何实际作用。这一切都是在 HTML 中完成的,有时会使用一些 JavaScript 代码进行微调

ARIA 的运作方式

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

为什么选择 ARIA?

我们为什么要对用户说谎!?

假设本地网上商店没有销售我们需要的所有小部件。可惜的是,我们是 MacGyver。我们只需使用其他 widget 自行创建 widget!FWIW,MacGyver 的七种常用物品是瑞士军刀、口香糖、鞋带、火柴、回形针、生日蜡烛和胶带。他用它们制造炸弹以及其他不只是放置在附近的物品。这与网络作者非常相似,他需要创建一个菜单栏。菜单栏非常实用,您以为它们是 HTML 的一部分,但实际上并非如此。好吧!您不认为作者会喜欢链接和按钮吗?因此,作者将使用他们喜爱的工具拼凑在一起,使用他们喜欢的工具:div、图片、样式、点击处理程序、按键处理程序、Spit 和 ARIA。

有时,我们并不会将 ARIA 发挥到极致,而只是将其当作一种增强功能。在一些已经基本有效的 HTML 上添加一些 ARIA 非常有用。例如,我们可能想要一个表单控件指向与一些无效输入相关的错误消息提醒。或者,我们可能想要指明某个文本框用于搜索。这些微小的改进能够让普通网站在屏幕阅读器中更易于使用。

支持点击鼠标的玩家

我们来一起创建一个菜单栏。我们在称为 div 的通用框元素中显示一系列项。无论何时,只要用户点击 div,就会执行相应的命令。太棒了,点击鼠标的用户也能使用它!

接下来,我们让它看起来很漂亮。我们使用 CSS,即样式,合理排列内容,并在内容周围放置视觉轮廓。我们让它看起来很像其他菜单栏,视觉障碍用户直观地知道它是一个菜单栏以及如何使用它。我们的菜单栏甚至可以针对鼠标悬停的任何项目使用不同的背景颜色,为用户提供一些有用的视觉反馈。

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

当然,这非常难以访问,就像网络上很多内容的常见情况一样,这主要是因为 HTML 标准向导没有添加网页制作者所需的一切。就算是,网络作者也始终想创作自己的特别版本。

使菜单栏的键盘可供使用

为了打造无障碍功能,首先,我们来添加键盘无障碍功能。此部分仅使用 HTML,不使用 ARIA。请注意,ARIA 不会影响没有辅助技术的用户的核心方面,例如外观、鼠标或键盘。

就像网页可以响应鼠标一样,网页也可以对键盘做出响应。我们的 JavaScript 会监听发生的所有按键操作,并确定相应按键是否有用。如果不是,它会像小到不能吃的鱼一样丢回网页。我们的规则如下:

  • 如果用户按箭头键,我们来看一下自己的内部菜单栏蓝图,并确定新的活动菜单项。我们将清除所有当前突出显示的内容,并突出显示新的菜单项,让视力正常的用户直观地了解自己所在的位置。然后,网页应调用 event.preventDefault() 以防止浏览器执行常规操作(在本例中为滚动网页)。
  • 如果用户按 Enter 键,我们可以将其视为点击,然后执行相应的操作(甚至打开另一个菜单)。
  • 如果用户按下的按键应执行其他操作,请勿吃该按键! 按照自然要求将其重新放回页面。例如,我们的菜单栏不需要 Tab 键,所以用回去吧!这很难理解,而且作者经常会搞乱它。例如,菜单栏需要箭头键,但不需要 Alt+箭头键Command+箭头键。这些快捷键用于移动到浏览器标签页的网络历史记录中的上一页/下一页。如果作者不够谨慎,菜单栏就会吃掉这些内容。这类错误经常出现,我们甚至还没有开始使用 ARIA!

通过屏幕阅读器访问菜单栏

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

但这不公平!菜单栏可以很好地满足视力正常的用户的需求。

这种情况下就可以派上 ARIA 了。ARIA 让我们可以假装屏幕阅读器的焦点位于菜单栏中。如果作者一切都正确无误,屏幕阅读器将看到我们的自定义菜单栏,就像桌面应用中的菜单栏一样。

第一个是“ARIA 谎言”:使用 aria-activedescendant 属性并将其设为当前活动菜单项的 ID,请注意每当它发生变化时都要小心更新。例如 aria-activedescendant="settings-menuitem"。这个白色小谎言会使屏幕阅读器将我们的 ARIA 活跃内容视为焦点,从而大声朗读或显示在盲文显示屏上。

ancestorancestorancestor的说明

“后代”一词是指一个项包含在另一个项中的某个位置。相反的术语是“祖先实体”,也就是说,某个项由祖先实体包含。对于下一个向上/向下容器,这些变量可以使用更具体的术语“父级/子级”。例如,假设某个文档包含一个段落,该段落内包含一个链接。链接的父级是一个段落,但也以该文档作为祖先实体。 反过来,文档可能会有多个段落子节点,每个段落都有链接。这些链接都是祖父文档的后代。

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

如果该菜单项可以引出子菜单呢?用户需要知道这一点,对吧?对于视力正常的用户,菜单末尾可能会有三角形的小图片,但至少在此时屏幕阅读器不知道如何自动读出图片。我们可以在每个可展开的菜单项上添加 aria-expanded="false",以表明 1) 有一些内容可以展开;2) 它当前未展开。为了进一步完善,作者应在图片三角形上添加 role="none",以表明它仅用于美化目的。这样可以防止屏幕阅读器读出与图片有关的任何信息,这些内容最多是多余的且可能令人厌烦。

处理 bug

键盘错误 (HTML!)

虽然键盘操作是核心 HTML 的一部分,但作者总是把它弄得乱糟,因为他们没有太多地使用键盘导航,或者是因为有太多的细微差别才能正确实现。

bug 示例:

  • 复选框使用空格键进行切换,但作者忘记调用 preventDefault()。现在,使用空格键可切换复选框和向下翻页,这是在浏览器上按住空格键的默认行为。
  • ARIA 模态对话框想要将标签页导航限制在其中,而作者忘记明确允许按 Control+Tab 键访问浏览器。现在,Ctrl+Tab 组合键只是在其对话框中导航,并未按预期在浏览器中切换标签页。呃,
  • 作者创建了一个选择列表,并实现了向上/向下箭头,但未实现 home/end/pageup/pagedown 或首字母导航。

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

对于纯键盘访问问题,在不使用屏幕阅读器或关闭虚拟浏览器模式的情况下尝试也很有用。屏幕阅读器通常不一定非要发现键盘错误,键盘访问实际上是通过 HTML(而不是 ARIA)实现的。毕竟,ARIA 不会影响键盘或鼠标行为等基本设置,它只是让屏幕阅读器了解网页中的内容、当前的焦点等等。

键盘 bug 几乎总是存在于 Web 内容中,特别是 HTML 和 JavaScript 中的 bug,而不是 ARIA 中的 bug。

ARIA bug:为什么有这么多?

在很多地方,作者可能会弄错 ARIA,而每个地方都会导致完全破坏或细微差异。细微差别可能更糟糕,因为作者在发布之前不会发现其中的大多数问题。

毕竟,除非作者是经验丰富的屏幕阅读器用户,否则 ARIA 中会出现问题。在我们的菜单栏示例中,作者可能会认为当“menuitem”正确时,应使用“option”角色。他们可能会忘记使用 aria-expanded,忘记在正确的时间设置和清除 aria-activedescendant,或者忘记有包含其他菜单的菜单栏。那么,菜品数量呢?通常,屏幕阅读器会以“第 3 项,共 5 项”之类的内容呈现菜单项,以便用户知道其所在位置。这通常由浏览器自动计数,但在某些情况下,以及在某些浏览器与屏幕阅读器组合中,可能会计算错误的数字,并且作者需要使用 aria-posinsetaria-setsize 替换这些数字。

这只是菜单栏。想一想有多少个微件。如有需要,您可以浏览 ARIA 规范或编写做法。对于每种模式,ARIA 被滥用的方式有十几种。ARIA 依靠作者了解他们正在做什么。考虑到大多数作者都不是屏幕阅读器用户,可能会出现哪些问题?

换言之,在 ARIA widget 被视为可交付之前,实际的屏幕阅读器用户完全需要尝试这些 widget。两者存在太多细微差别。理想情况下,由于存在众多的实现怪异以及一些不完整的实现,因此尝试使用几种不同的浏览器屏幕阅读器组合来尝试执行所有操作。

摘要

总而言之,ARIA 魔法命令可用于覆盖或添加 HTML 内容的所有内容。 它可用于对无障碍功能呈现方式进行细微更改,或打造完整的体验。正因如此,在我们友好的本地网络作者(通常不使用屏幕阅读器)手里,ARIA 既强大又危险。

ARIA 只是一个简单直接的真实值替换标记层。当屏幕阅读器询问发生了什么时,如果存在 ARIA,他们会看到 ARIA 版本的真相,而不是真正的底层真相。

附录 1:其他资源

包含键盘信息和代码示例的混合参考

  • W3C 的 ARIA 编写做法:本文档记录了每个示例的重要键盘导航特性,并提供有效的 JS/CSS/ARIA 代码。这些示例侧重于目前有效的方法,没有涵盖移动设备。

附录 2:ARIA 最常用于哪些用途?

因为 ARIA 可以替换或补充大大小小的事实,通常对于读出屏幕阅读器关心的内容非常有用。

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

  • HTML 中不存在的特殊微件,例如菜单栏、自动补全、树或电子表格
  • 存在于 HTML 中的 widget,但作者仍然发明了自己的 widget,这可能是因为它们需要调整常规 widget 的行为或外观。例如,HTML <input type="range"> 元素基本上就是一个滑块,但作者希望它看起来有所不同。对于大多数情况,可以使用 CSS,但对于 input type="range",使用 CSS 就不太美观了。作者可以创建自己的滑块,然后结合使用 role="slider"aria-valuenow 来指示当前值是什么。
  • 动态区域会告知屏幕阅读器“在该页面的此区域中,任何更改都值得通知用户”。
  • 地标(HTML 现在有等效项)。这些标题有点像标题,可以帮助屏幕阅读器用户更快地找到所需内容。不过,它们的不同之处在于它们包含整个相关区域。例如,“这个容器是页面的主要区域”和“此处的这个容器是导航面板”。

附录 3:什么是无障碍功能 API?

无障碍功能 API 是屏幕阅读器或其他 AT 如何知道页面中的内容以及当前发生的情况。例如 MSAA、IA2 和 UIA。这只是 Windows 系统!无障碍功能 API 包含两个部分:

  • 表示容器层次结构的对象“树”。这种玩偶就像俄罗斯套娃,但每个玩偶可以包含多个其他玩偶。例如,一个文档可以包含多个段落,一个段落可以包含文本、图片、链接、粗体等。对象树中的每个项都可以具有角色(我是什么?)、名称/标签、用户输入的值、说明以及布尔状态(如可聚焦、聚焦、必需、选中等)等属性。ARIA 可以替换上述任何属性。 屏幕阅读器使用树帮助用户在虚拟缓冲区模式下导航,例如“请前往下一个标题”。
  • 发生的一系列描述树形变化的事件,例如“焦点现在已移到此处!”。屏幕阅读器使用事件来告诉用户刚刚发生了什么。当重要的 HTML 或 ARIA 标记发生更改时,系统会触发一个事件,以告知屏幕阅读器某些内容发生了更改。

通常,作者只使用 HTML,它能够很好地映射到这些无障碍功能 API。当 HTML 不够用时,系统会使用 ARIA,并且浏览器会先替换 HTML 语义,然后再将对象树或事件发送给屏幕阅读器。