Chrome DevTools 将推出完整的无障碍功能树,让开发者更轻松地大致了解整个树。在本文中,了解如何创建此树以及如何在工作中使用它。
什么是无障碍树?
屏幕阅读器等辅助技术使用 Chromium 的无障碍 API 与网页内容互动。此 API 的底层模型是无障碍功能树:一个由无障碍功能对象组成的树,辅助技术可以查询其属性和特性并执行操作。Web 开发者主要通过 DOM 属性(例如适用于 HTML 的 ARIA 属性)来塑造和操控无障碍树。
在 Chrome DevTools 中,我们提供了无障碍功能窗格,以帮助开发者了解其内容如何向辅助技术展示。具体而言,在 DOM 树查看器中选择某个节点后,相应无障碍节点的属性会显示在该窗格中,同时还会显示该节点的祖先节点和其直接子节点的视图。
该树是如何创建的?
在介绍此全新完整树状视图在 DevTools 中的显示方式之前,我们先来简要介绍一下无障碍功能树的具体含义。无障碍树是 DOM 树的派生树。其结构大致相同,但经过简化,移除了没有语义内容的节点,例如仅用于样式的 <div>
元素。树中的每个节点都有一个角色(例如 Button
或 Heading
),通常还具有一个名称,该名称可以来自 ARIA 属性,也可以从节点的内容派生而来。如果我们查看 HTML 文档:
<html>
<head>
<title>How old are you?</title>
</head>
<body>
<label for="age">Age</label>
<input id="age" type="number" name="age" value="42">
<div>
<button>Back</button>
<button>Next</button>
</div>
</body>
</html>
Chromium 中的渲染程序(名为 Blink)会派生出一个内部无障碍树,大致如下所示。
role='rootWebArea' focusable name='How old are you?'
role='genericContainer' ignored
role='genericContainer' ignored
role='labelText'
role='staticText' name='Age'
role='spinButton' editable focusable name='Age' value='42'
role='genericContainer' editable
role='staticText' editable name='42'
role='genericContainer'
role='button' focusable name='Back'
role='staticText' name='Back'
role='button' focusable name='Next'
role='staticText' name='Next'
请注意,此表示法包含多个角色为 genericContainer
的多余节点,这似乎与上述关于无障碍树是 DOM 树的简化派生树的说法相矛盾。不过,大多数此类节点仅出现在内部树中,不会向辅助技术显示。由于开发者工具会直接从渲染程序进程收集无障碍信息,因此这是开发者工具处理的树表示法。
开发者工具中的完整无障碍功能树
新的完整无障碍功能树与 DOM 树同步,以便开发者在两个树之间切换。我们希望新树状结构更易于探索、更实用且更易于使用。
现在,您已经了解无障碍功能树的运作方式,可以使用 DevTools 查看新树状视图的外观。以下包含标题、标题和两个按钮的 HTML 文档用于显示树状结构。
<!DOCTYPE html>
<title>Test</title>
<h1>Heading for example page</h1>
<div>
<button>Back</button>
<button>Next</button>
</div>
以前的树状视图仅允许您浏览单个节点及其祖先节点。
现在,当您切换新树时,它会取代 DOM 树视图,让您能够查看网页的完整无障碍功能树:
延迟树构建
为了确保树即使在大型网站上也能高效运行,系统会在前端探索树时延迟构建树。在树中展开某个节点后,系统会通过 Chrome 开发者工具协议 (CDP) 提取该节点的子节点,并重新构建树。
直播
新的树状视图是实时的,如果渲染程序中的无障碍树发生变化,则会动态更新。它会钩接到用于通知辅助技术树发生变化的相同机制,并使用该机制向 DevTools 前端发出包含更新后的节点的事件。在实践中,CDP 后端会监听树的更新,跟踪之前请求过哪些节点,并在这些节点中的任何一个发生更改时向 DevTools 前端发送事件。
众多树木的故事
在无障碍功能树是什么的说明中,您了解了 Blink 如何为其正在渲染的 DOM 构建无障碍功能树,以及 DevTools 如何通过 CDP 提取此树。虽然这确实如此,但我们在说明中忽略了一些复杂情况。实际上,在 Chromium 中体验无障碍功能树的方式有很多。在为 DevTools 设计新的树视图时,我们在 Chromium 无障碍功能内部结构的哪些部分中显示了一些内容。
平台
每个平台都有不同的无障碍功能 API,虽然树的形状在所有平台上都是相同的,但用于与树交互的 API 不同,属性的名称也可能不同。开发者工具会显示 Chromium 的内部树,其中角色和属性通常与 ARIA 规范中定义的角色和属性相匹配。
多个框架和网站隔离
由于 Chromium 不仅会将每个标签页的内容放入不同的渲染器进程中,还会将跨网站文档隔离在不同的渲染器进程中,因此我们必须通过 CDP 分别连接到每个进程外子文档,并提取其无障碍功能树。然后,我们会在前端将这些子树拼接在一起,以便呈现出一个连贯的树的错觉,尽管它们位于 Chromium 中的不同渲染程序中。
被忽略的节点和无趣的节点
默认情况下,我们会隐藏一些节点:被忽略的节点,以及角色为“通用”且没有名称的节点。这些节点没有语义含义,对于被忽略的节点,系统不会向辅助技术显示这些节点。我们会隐藏这些节点,以免树状视图过于杂乱。如果不这样做,大多数网页的无障碍功能树将会如下所示:
这里需要注意的是,这实际上意味着我们需要构建一个与后端不同的树。例如,假设我们有节点 A、B、C 和 X,其中 A 有子节点 X 和 B,X 有子节点 C。如果 X 是被忽略的节点,我们会从树中剪除 X,并改为创建一个树,其中 C 是 A 的子节点。
在前端,我们会构建包含被忽略节点的完整树,并且仅在渲染节点之前对其进行修剪。我们之所以这样做,有两个原因:
- 这样,从后端处理节点更新会变得更加简单,因为这两个端点具有相同的树结构。例如,如果在示例中移除节点 B,我们会收到节点 X 的更新(因为其子节点已更改),但如果我们修剪了该节点,则很难确定要更新什么。
- 它可确保所有 DOM 节点都有对应的无障碍节点。切换树时,我们会选择与 DOM 树中当前选定的节点对应的节点。因此,对于前面的示例,如果用户在选择与 X 对应的 DOM 节点时切换树,我们会在节点 A 和 B 之间注入 X,并在树中选择 X。这样,用户就可以检查所有 DOM 节点的无障碍节点,并帮助确定节点被忽略的原因。
未来想法
推出新的无障碍树只是第一步。我们有一些想法,希望在未来的项目中基于新视图进行构建,但也非常期待听取您的反馈!
备选过滤条件
如上所述,我们目前会滤除被视为不感兴趣的节点。我们可以提供一种方法来停用此行为并显示所有节点,或者提供其他过滤条件,例如显示地图注点或显示标题。
突出显示无障碍问题
我们可以将“无障碍功能最佳实践”分析纳入树中,并直接在有问题的节点上突出显示无障碍功能问题。
在 DevTools 中显示无障碍功能操作
我们目前显示的树状结构是单向的:它可让我们大致了解浏览特定网页时会向辅助技术提供哪些信息。无障碍功能操作代表了另一个方向的通信:它们允许辅助技术对所呈现的界面执行操作。我们可以在 DevTools 中显示此类操作,以允许使用辅助技术 API 执行“点击”“滚动”或更改页面上值等操作。