Chrome 开发者工具中的完整无障碍功能树

Johan Bay
Johan Bay

Chrome DevTools 将推出完整的无障碍功能树,让开发者更轻松地大致了解整个树。在本文中,了解如何创建此树以及如何在工作中使用它。

什么是无障碍树?

屏幕阅读器等辅助技术使用 Chromium 的无障碍功能 API 与网页内容互动。此 API 的基础模型是无障碍树:辅助技术可以查询属性和属性并对其执行操作的无障碍对象树。Web 开发者主要通过 DOM 属性(例如适用于 HTML 的 ARIA 属性)来塑造和操控无障碍树。

Chrome DevTools 中,我们提供了无障碍功能窗格,以帮助开发者了解其内容如何向辅助技术展示。具体而言,在 DOM 树查看器中选择某个节点后,相应无障碍节点的属性会显示在该窗格中,同时还会显示该节点的祖先节点和其直接子节点的视图。

Chrome 开发者工具的无障碍功能窗格。

该树是如何创建的?

在介绍此全新完整树状视图在 DevTools 中的显示方式之前,我们先来简要介绍一下无障碍功能树的具体含义。无障碍树是 DOM 树的派生树。其结构大致相同,但经过简化,移除了没有语义内容的节点,例如仅用于样式的 <div> 元素。树中的每个节点都有一个角色(例如 ButtonHeading),通常还具有一个名称,该名称可以来自 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 树同步,以便开发者在两棵树之间来回切换。我们希望新树状结构更易于探索、更实用且更易于使用。

现在,您已了解无障碍树的工作原理,您可以使用开发者工具来查看新树状视图的外观。以下 HTML 文档使用了标题、标题和两个按钮来显示树。

<!DOCTYPE html>
<title>Test</title>
<h1>Heading for example page</h1>
<div>
  <button>Back</button>
  <button>Next</button>
</div>

以前的树状视图仅允许您浏览单个节点及其祖先节点。

开发者工具中的上一个树状视图。

现在,当您切换新树时,它会取代 DOM 树视图,并允许您查看页面的完整无障碍功能树:

DevTools 中的新树状视图。

延迟树构建

为使该树在较大的站点上也能保持良好的性能,在探索该树时,会在前端延迟构造该树。在树中展开某个节点后,系统会通过 Chrome 开发者工具协议 (CDP) 提取该节点的子节点,并重新构建树。

显示大页面结果的全新无障碍功能树。

直播

新的树状视图是实时的,如果渲染程序中的无障碍树发生变化,该视图会动态更新。它与向辅助技术发出树更改通知的机制相同,并使用该机制向具有更新节点的开发者工具前端发送事件。 实际上,CDP 后端会监听树的更新,跟踪之前请求了哪些节点,并在任何节点发生变化时将事件发送到开发者工具前端。

关于许多树木的故事

什么是无障碍功能树的说明中,您学习了 Blink 如何为它正在渲染的 DOM 构建无障碍功能树,而开发者工具会通过 CDP 获取此树。虽然这是事实,但我们在说明中略去了一些复杂问题。实际上,在 Chromium 中体验无障碍功能树的方式有很多。在为开发者工具设计新的树视图时,我们在 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 中显示无障碍功能操作

我们目前显示的树状结构完全是单向的:它可让我们大致了解浏览特定网页时会向辅助技术提供哪些信息。无障碍操作代表相反方向的通信:它们允许辅助技术对呈现的界面执行操作。我们可以在开发者工具中显示此类操作,以便使用适用于辅助技术的 API 执行“点击”、滚动或更改页面上的值等操作。