使用 tabindex

使用 tabindex 修改 DOM 顺序

Dave Gash
Dave Gash
Meggin Kearney
Meggin Kearney

原生元素的 DOM 位置提供的默认标签页顺序很方便,但有时您需要修改 Tab 键顺序,而在 HTML 中以物理方式移动元素并不一定是最佳甚至可行的解决方案。在这些情况下,您可以使用 tabindex HTML 属性来明确设置元素的标签页位置。

浏览器支持

  • 1
  • 12
  • 1.5
  • 不超过 4

来源

tabindex 可应用于任何元素(但不一定对每个元素都很有用),它接受一系列整数值。借助 tabindex,您可以为可聚焦页面元素指定明确的顺序,将原本无法聚焦的元素插入到 Tab 键顺序中,以及从 Tab 键顺序中移除元素。例如:

tabindex="0":在自然 Tab 键顺序中插入元素。您可以通过按 Tab 键聚焦该元素,也可以通过调用其 focus() 方法聚焦该元素

<custom-button tabindex="0">Press Tab to Focus Me!</custom-button>

按 Tab 键即可聚焦于我!

tabindex="-1":从自然 Tab 键顺序中移除某个元素,但仍可通过调用其 focus() 方法聚焦该元素

// TODO: DevSite - Code sample removed as it used inline event handlers

// TODO:DevSite - 代码示例由于使用了内嵌事件处理脚本而被移除

tabindex="5":只要 tabindex 大于 0,就会将该元素跳转到自然 Tab 键顺序的最前面。如果有多个元素的 tabindex 均大于 0,则 Tab 键顺序从大于 0 的最小值开始,从低到高的顺序排列。使用大于 0 的 tabindex 被视为反模式。

<button>I should be first</button>
<button>And I should be second</button>
<button tabindex="5">But I jumped to the front!</button>

对于标题、图片或文章标题等非输入元素,尤其如此。为此类元素添加 tabindex 会适得其反。如有可能,最好对您的源代码进行排列,以便 DOM 序列提供符合逻辑的 Tab 键顺序。如果您确实要使用 tabindex,请将其限制为按钮、标签页、下拉列表和文本字段等自定义互动控件;也就是说,用户可能希望为其提供输入的元素。

不必担心屏幕阅读器用户会错过重要内容,因为它没有 tabindex。即使内容非常重要(如图片),但如果不是用户可以与之互动的内容,也没有理由将其设为可聚焦。只要您提供适当的 alt 属性支持(我们很快就会介绍),屏幕阅读器用户仍然可以理解图片的内容。

在页面级别管理焦点

在本例中,tabindex 不仅有用,还必不可少。您可能正在构建一个包含不同内容部分(并非所有部分都同时可见)的强大单个网页。在此类网页中,点击导航链接可能会更改可见内容,而不刷新网页。

发生这种情况时,您可能需要确定所选内容区域,为其指定 -1 的 tabindex,使其不会以自然的 Tab 键顺序显示,然后调用其 focus 方法。这种方法称为“管理焦点”,可使用户的感知上下文与网站的视觉内容保持同步。

管理组件中的焦点

更改页面上的内容时管理焦点很重要,但有时您需要在控件级别管理焦点 - 例如,在构建自定义组件时。

以原生 select 元素为例。它可以获得基本焦点,但获得焦点后,您就可以使用箭头键来公开其他功能(可选选项)。如果您构建的是自定义 select 元素,则需要公开此类行为,以便主要依赖键盘的用户仍然可以与您的控件互动。

<!-- Focus the element using Tab and use the up/down arrow keys to navigate -->
<select>
    <option>Aisle seat</option>
    <option>Window seat</option>
    <option>No preference</option>
</select>

知道要实现哪些键盘行为可能并非易事,但您可以参阅一份有用的文档。无障碍富互联网应用 (ARIA) 编写做法指南列出了组件类型以及它们支持的键盘操作类型。我们稍后会更详细地介绍 ARIA,但现在,我们借助本指南来为新组件添加键盘支持。

也许您正在处理一些新的自定义元素,它们类似于一组单选按钮,但具有您对外观和行为的独特风格。

<radio-group>
    <radio-button>Water</radio-button>
    <radio-button>Coffee</radio-button>
    <radio-button>Tea</radio-button>
    <radio-button>Cola</radio-button>
    <radio-button>Ginger Ale</radio-button>
</radio-group>

如需确定他们需要哪种键盘支持,请参阅 ARIA 创作做法指南。第 2 部分包含一系列设计模式,该列表中是单选按钮组的特征表,其中存在与新元素最匹配的现有组件。

如上表所示,应该支持的常见键盘行为之一是向上/向下/向左/向右箭头键。为了将此行为添加到新组件,我们将采用一种称为“流动 tabindex”的方法。

单选按钮的 W3C 规范摘录。

流动 tabindex 通过将除当前活跃子项之外的所有子项的 tabindex 设置为 -1 来发挥作用。

<radio-group>
    <radio-button tabindex="0">Water</radio-button>
    <radio-button tabindex="-1">Coffee</radio-button>
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

然后,该组件会使用键盘事件监听器来确定用户按的是哪个键;发生此情况时,它会将之前获得焦点的子项的 tabindex 设为 -1,将要聚焦的子项的 tabindex 设为 0,并对其调用 focus 方法。

<radio-group>
    // Assuming the user pressed the down arrow, we'll focus the next available child
    <radio-button tabindex="-1">Water</radio-button>
    <radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

当用户到达最后一个(或第一个,具体取决于其移动焦点的方向)子项时,您将循环切换,并再次聚焦第一个(或最后一个)子项。

您可以试着在下方试一试完成的示例。在开发者工具中检查该元素,观察 tabindex 从一个单选按钮到下一个单选按钮的移动情况。

咖啡 可乐 姜汁啤酒

// TODO:DevSite - 代码示例由于使用了内嵌事件处理脚本而被移除

您可以在 GitHub 上查看此元素的完整源代码

模态和键盘陷阱

当你管理焦点时,有时可能会遇到无法退出的情况。假设有一个 Autocomplete widget,它尝试管理焦点并捕获标签页行为,但阻止用户在完成之前离开。这种行为称为键盘陷阱,可能会让用户感到非常沮丧。 Web AIM 核对清单的第 2.1.2 部分解决了此问题,指出键盘焦点绝不应被锁定或被限制在一个特定页面元素上。用户应该能够只使用键盘就能在所有页面元素之间导航。

奇怪的是,有时此行为实际上是可取的,例如在模态窗口中。通常,显示模态窗口时,您不希望用户访问其背后的内容。您可以添加一个叠加层,在视觉上覆盖页面,但并不能阻止键盘焦点意外地离开模态窗口。

要求用户保存工作的模态窗口。

在此类情况下,您可以实现一个临时键盘陷阱,以确保仅在模态显示时捕获焦点,然后在模态窗口关闭时将焦点恢复到之前聚焦的项。

有一些提案(包括 <dialog> 元素)让开发者能更轻松地做到这一点,但它们还没有得到广泛的浏览器支持。

如需详细了解 <dialog>,请参阅这篇 MDN 文章;如需详细了解模态窗口,请参阅这篇模态窗口示例

假设有一个模态对话框,它由一个包含几个元素的 div 和另一个表示背景叠加层的 div 表示。我们来了解一下在这种情况下实现临时键盘陷阱所需的基本步骤。

  1. 使用 document.querySelector,选择模态和叠加层 div 并存储其引用。
  2. 在模态窗口打开时,存储对当模态窗口打开时获得焦点的元素的引用,以便将焦点返回到该元素。
  3. 使用 keydown 监听器在模态窗口打开时抓取按键。您还可以监听背景叠加层上的点击操作,在用户点击它时关闭模态窗口。
  4. 接下来,获取模态窗口中可聚焦元素的集合。第一个和最后一个可聚焦元素将充当“哨兵”,让您知道何时向前或向后循环聚焦以留在模态窗口内。
  5. 显示模态窗口并聚焦第一个可聚焦元素。
  6. 当用户按 TabShift+Tab 时,向前或向后移动焦点,视情况在最后一个或第一个元素处循环播放。
  7. 如果用户按 Esc,请关闭模态窗口。这非常有用,因为它可让用户关闭模态窗口,而无需搜索特定关闭按钮,甚至连鼠标用户也能从中受益。
  8. 当模态窗口关闭时,隐藏模态窗口和背景叠加层,然后将焦点恢复到之前保存的之前聚焦的元素。

此过程为您提供了一个不会令人失望的易用模态窗口,可供所有人有效使用。

如需了解详情,您可以查看此示例代码,并通过已完成的页面查看实际示例。