使用 tabindex 控制焦点
标准 HTML 元素(如 <button>
或 <input>
)免费内置了键盘可访问性。不过,如果您正在构建自定义交互组件,请使用 tabindex
属性确保它们可以通过键盘访问。
检查您的控件是否可通过键盘访问 #
像 Lighthouse 这样的工具非常适合检测某些可访问性问题,但有些东西只能由人来测试。
尝试按 Tab
键浏览您的网站。您是否能够访问页面上的所有交互式控件?如果不能,您可能需要使用 tabindex
来提高这些控件的可聚焦性。
将元素插入到 Tab 键顺序中 #
使用 tabindex="0"
将元素插入到自然的 Tab 键顺序中。例如:
<div tabindex="0">Focus me with the TAB key</div>
要聚焦某个元素,请按 Tab
键或调用该元素的 focus()
方法。
从 Tab 键顺序中删除元素 #
使用 tabindex="-1"
删除元素。例如:
<button tabindex="-1">Can't reach me with the TAB key!</button>
这将从自然的 Tab 键顺序中删除一个元素,但仍然可以通过调用该元素的 focus()
方法来聚焦该元素。
请注意,对元素应用 tabindex="-1"
不会影响其子元素;如果它们本来就处在 Tab 键顺序中或由于 tabindex
值的原因,它们将保留在 Tab 键顺序中。要从 Tab 键顺序中删除一个元素及其所有子元素,请考虑使用 WICG 的 inert
polyfill。该 polyfill 模拟提议的 inert
属性的行为,可防止元素被辅助技术选择或读取。
避免 tabindex > 0
#
任何大于 0 的 tabindex
都会使元素跳到自然 Tab 键顺序的前面。如果有多个元素的 tabindex
大于0,则 Tab 键顺序从大于 0 的最低值开始,依次向上。
使用大于 0 的 tabindex
被认为是一种反模式,因为屏幕阅读器按 DOM 顺序导航页面,而不是按 Tab 键顺序。如果您需要某个元素在 Tab 键顺序中更早出现,则应将其移至 DOM 中较早的位置。
使用 Lighthouse 可以轻松识别 tabindex
> 0 的元素。运行可访问性审计(Lighthouse > Options(选项)> Accessibility(可访问性))并查找“没有元素的 [tabindex] 值大于 0”审计的结果。
通过“漫游 tabindex
”创建可访问组件 #
如果您正在构建一个复杂的组件,您可能需要添加除聚焦之外的额外键盘支持。请考虑内置的 select
元素。它是可聚焦的元素,并且您可以使用箭头键来揭示附加功能(可选选项)。
要在您自己的组件中实现类似功能,请使用一种称为“漫游 tabindex
”的技术。漫游 tabindex 的工作原理是将所有子元素的 tabindex
设置为 -1,但当前活动的子元素除外。该组件随后使用键盘事件侦听器来确定用户按下了哪个键。
当发生按键事件时,组件将先前被聚焦的子元素的 tabindex
设置为 -1,将要被聚焦的子元素的 tabindex
设置为 0,然后对其调用 focus()
方法。
之前
<div role="toolbar">
<button tabindex="-1">Undo</div>
<button tabindex="0">Redo</div>
<button tabindex="-1">Cut</div>
</div>
之后
<div role="toolbar">
<button tabindex="-1">Undo</div>
<button tabindex="-1">Redo</div>
<button tabindex="0">Cut</div>
</div>
This HTML renders a modal dialog:
<div role="dialog" aria-labelledby="dialog-header">
<button aria-label="Close"></button>
<h2 id="dialog-header">
Do you want to allow notifications from this website?
</h2>
<button>No</button>
<button>Yes</button>
</div>
What is the tab order for the elements in the sample?
- The Close button
- The No button
- The Yes button
Only the <button>
elements are included in the tab order because they're the only standardized HTML form elements. To insert other elements into the tab order, you would add a tabindex
attribute.
<section tabindex="-1">
<h2>Cat facts</h2>
<ul>
<li>A group of cats is called a <a href="https://m-w.com/dictionary/clowder">clowder</a>.</li>
<li>Most cats are <a href="https://www.catfacts.org/catnip.html"> unaffected by catnip</a>.</li>
</ul>
</section>
Which elements from the sample are included in the tab order?
Only the <a>
elements are included in the tab order.
The <section>
element is not in the tab order because it has a negative tabindex
value. (It can, however, be focused using the focus()
method.) The tabindex
value for the <section>
element doesn't affect its children.
This HTML renders a popup menu followed by a search input:
<div role="menu" tabindex="0">
<a role="menuitem" href="/learn/" tabindex="-1">Learn</a>
<a role="menuitem" href="/measure/" tabindex="-1">Measure</a>
<a role="menuitem" href="/blog/" tabindex="-1">Blog</a>
<a role="menuitem" href="/about/" tabindex="-1">About</a>
</div>
<input tabindex="1" type="text" role="search" aria-label="Search" placeholder="Search">
Which element in the sample comes first in the tab order?
The Search text input comes first in the tab order. Because it has a tabindex
greater than zero, it jumps to the front of the tab order.
(This behavior is likely to cause confusion if the menu is positioned on the page before the search input. This is an example of why having a tabindex
value greater than zero is considered an anti-pattern.)
This HTML renders a custom radio group, which should have a roving tabindex
. (To keep things simpler, ignore the aria-*
attributes for now.)
<div role="radiogroup" aria-labelledby="breed-header">
<h3 id="breed-header">Your cat's breed</h3>
<div role="radio" aria-checked="false" tabindex="0">Persian</div>
<div role="radio" aria-checked="false" tabindex="-1">Bengal</div>
<div role="radio" aria-checked="false" tabindex="-1">Maine Coon</div>
</div>
When a role="radio"
element is focused, what should happen when a user presses the Right
arrow key?
- Change the
tabindex
values for all radio elements in the group to -1. - If there's a radio element after the one that's focused, set its
tabindex
value to 0. - If there's no radio element after the one that's focused, set the
tabindex
value of the first radio element in the group to 0. - Focus the radio element that now has a
tabindex
of 0.
That's a lot—and it doesn't even include ARIA attributes! This is an example of why it's easier to use built-in elements with built-in keyboard behavior whenever you can.
键盘访问秘诀 #
如果您不确定您的自定义组件可能需要什么级别的键盘支持,可以参考 ARIA 创作实践 1.1。这个方便的指南列出了常见的 UI 模式并确定您的组件应该支持哪些键。