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

约翰湾
Johan Bay

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

什么是无障碍树?

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

Chrome 开发者工具中,我们提供了无障碍功能窗格,以帮助开发者了解其内容如何向辅助技术公开。具体而言,在 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 树的简化导数相矛盾。不过,其中大多数节点只发生在内部树中,不会用于辅助技术。由于开发者工具直接从渲染器进程收集其无障碍功能信息,因此这是开发者工具处理的树状表示形式。

DevTools 中的完整无障碍功能树

全新的完整无障碍功能树可与 DOM 树同步,因此开发者可在这两种树之间来回切换。我们希望新树的可探索性更强、更实用、更易于使用。

现在,您已经了解了无障碍功能树的运作方式,接下来可以使用开发者工具来查看新的树状视图的外观。以下 HTML 文档(包含标题、标题和两个按钮)用于显示树。

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

之前的树状视图只能探索单个节点及其祖先。

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

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

开发者工具中的新树状视图。

延迟树构建

为了即使对于大型网站也提高该树的性能,在探索该树时,它会在前端进行延迟构建。在树中展开节点后,系统会通过 Chrome 开发者工具协议 (CDP) 提取这些节点的子节点,然后重新构建树。

显示大网页结果的新无障碍树。

直播

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

关于许多树木的故事

在介绍什么是无障碍功能树时,您了解了 Blink 如何为它正在呈现的 DOM 构建无障碍功能树,以及开发者工具如何通过 CDP 提取该树。尽管这是事实,但我们在此说明中省略了一些复杂问题。实际上,您可以通过很多不同的方式来体验 Chromium 中的无障碍树。在为开发者工具设计新的树状视图时,我们已就希望呈现 Chromium 无障碍功能内部元素的哪些部分做出了选择。

平台

每个平台都有不同的无障碍功能 API,虽然树的形状在所有平台上都是相同的,但用于与树互动的 API 是不同的,并且属性名称也可能不同。开发者工具显示了 Chromium 的内部树,其中的角色和属性往往与 ARIA 规范中定义的内容保持一致。

多框架和网站隔离

由于 Chromium 不仅会将每个标签页的内容放在不同的渲染程序进程中,还会在不同的渲染程序进程中隔离跨网站文档,因此我们必须通过 CDP 分别连接到每个进程外子文档,并获取其可访问性树。然后,我们会将这些子树拼接到前端,呈现一种连贯的树,尽管它们位于 Chromium 中的不同渲染器进程中。

已忽略且无趣的节点

默认情况下,我们会隐藏一些节点:已忽略的节点,以及角色为“generic”且没有名称的节点。这些节点不具有语义含义,对于忽略的节点,这类节点不会提供给辅助技术。我们会隐藏这些节点以避免让树状视图杂乱无章。否则,大多数网页的无障碍树将如下所示:

显示所有节点的新树状视图。

需要注意的是,这实质上意味着我们需要构建另一个树,而不是后端可用的树。例如,假设我们有节点 A、B、C 和 X,其中 A 有子节点 X 和 B,而 X 有子节点 C。如果 X 是一个忽略的节点,我们从该树中剪除 X,并改为创建一个树,其中 C 是 A 的子节点。

显示我们如何修剪树的示意图。

在前端,我们构建包含忽略节点的完整树,并且仅在渲染节点之前剪除它们。这样做有两个原因:

  • 从后端处理节点更新要简单得多,因为两个端点都采用相同的树结构。例如,如果在示例中移除节点 B,我们将收到节点 X 的更新(因为其子节点已更改),但如果我们删减了该节点,我们便很难确定需要更新什么。
  • 它可确保所有 DOM 节点都有对应的无障碍功能节点。切换树时,我们选择与 DOM 树中当前选定的节点对应的节点。因此,对于前面的示例,如果用户在选择与 X 对应的 DOM 节点时切换了树,我们会在节点 A 和 B 之间注入 X,并在节点 A 和节点 B 之间注入 X,并在树中选择 X。这样,用户就可以检查所有 DOM 节点的无障碍功能节点,并帮助确定忽略该节点的原因。

未来的创意

发布新的无障碍功能树仅仅是一个开始。对于今后可以基于此新视图进行构建的项目,我们提供了一些创意,但也非常期待收到您的反馈

替代过滤条件

如上所述,我们目前会过滤掉被视为无趣的节点。我们可以提供一种方式来停用此行为并显示所有节点,或者提供替代过滤,例如显示地标节点显示标题

突显无障碍问题

我们可以在树中纳入“无障碍功能最佳实践”分析,并直接在违规节点上突出显示无障碍功能问题。

在开发者工具中显示无障碍操作

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