支持键盘导航的 JavaScript 组件
Web 应用程序通常使用 JavaScript 来模拟桌面组件,例如菜单、树形视图、富文本字段和选项卡面板。这些组件通常由 <div>
和 <span>
元素组成,它们本身并不提供与其桌面对应组件相同的键盘功能。本文档介绍了使 JavaScript 组件可以通过键盘访问的技术。
使用 tabindex
默认情况下,当用户使用 Tab 键浏览网页时,只有交互式元素(如链接、表单控件)会获得焦点。使用 tabindex
全局属性,作者也可以使其他元素可聚焦。当设置为 0
时,该元素可以通过键盘和脚本获得焦点。当设置为 -1
时,该元素可以通过脚本获得焦点,但不会成为键盘焦点顺序的一部分。
使用键盘时元素获得焦点的顺序默认情况下是源顺序。在特殊情况下,作者可能希望重新定义顺序。为此,作者可以将 tabindex
设置为任何正数。
警告:避免为 tabindex
使用正值。具有正 tabindex
的元素放在页面上默认交互式元素之前,这意味着页面作者在使用一个或多个正 tabindex
值时,必须为页面上的所有可聚焦元素设置(并维护)tabindex
值。
下表描述了现代浏览器中 tabindex
的行为
tabindex 属性 |
可通过鼠标或 JavaScript(通过 element.focus() )获得焦点 |
可通过 Tab 键导航 |
---|---|---|
不存在 | 遵循元素的平台约定(表单控件、链接等为是)。 | 遵循元素的平台约定。 |
负数(即 tabindex="-1" ) |
是 | 否;作者必须在响应箭头键或其他按键时,使用 focus() 使元素获得焦点。 |
零(即 tabindex="0" ) |
是 | 在文档中元素位置相关的 Tab 顺序中(请注意,像 <a> 这样的交互式元素默认具有此行为,它们不需要此属性)。 |
正数(例如 tabindex="33" ) |
是 | tabindex 值决定此元素在 Tab 顺序中的位置:较小的值会使元素在 Tab 顺序中比较大的值更早出现(例如,tabindex="7" 将位于 tabindex="11" 之前)。 |
非原生控件
分组控件
对于菜单、选项卡列表、网格或树形视图等分组组件,父元素应位于 Tab 顺序中(tabindex="0"
),并且每个子元素选择/选项卡/单元格/行应从 Tab 顺序中移除(tabindex="-1"
)。用户应该能够使用箭头键导航子元素。(有关通常预期用于典型组件的键盘支持的完整描述,请参阅 WAI-ARIA 创作实践)。
下面的示例展示了这种技术如何与嵌套菜单控件一起使用。一旦键盘焦点落在包含 <ul>
元素上,JavaScript 开发人员必须以编程方式管理焦点并响应箭头键。有关在组件内管理焦点的技术,请参阅下面的“管理组内的焦点”。
<ul id="mb1" tabindex="0">
<li id="mb1_menu1" tabindex="-1">
Font
<ul id="fontMenu" title="Font" tabindex="-1">
<li id="sans-serif" tabindex="-1">Sans-serif</li>
<li id="serif" tabindex="-1">Serif</li>
<li id="monospace" tabindex="-1">Monospace</li>
<li id="fantasy" tabindex="-1">Fantasy</li>
</ul>
</li>
<li id="mb1_menu2" tabindex="-1">
Style
<ul id="styleMenu" title="Style" tabindex="-1">
<li id="italic" tabindex="-1">Italics</li>
<li id="bold" tabindex="-1">Bold</li>
<li id="underline" tabindex="-1">Underlined</li>
</ul>
</li>
<li id="mb1_menu3" tabindex="-1">
Justification
<ul id="justificationMenu" title="Justification" tabindex="-1">
<li id="left" tabindex="-1">Left</li>
<li id="center" tabindex="-1">Centered</li>
<li id="right" tabindex="-1">Right</li>
<li id="justify" tabindex="-1">Justify</li>
</ul>
</li>
</ul>
禁用控件
当自定义控件被禁用时,通过设置 tabindex="-1"
将其从 Tab 顺序中移除。请注意,分组组件中的禁用项(例如菜单中的菜单项)应能够继续使用箭头键进行导航。
管理组内的焦点
当用户从组件切换到其他位置再返回时,焦点应返回到具有焦点的特定元素,例如树项或网格单元格。有两种技术可以实现这一点
- 游动
tabindex
:以编程方式移动焦点 aria-activedescendant
:管理“虚拟”焦点
技术 1:游动 tabindex
将聚焦元素的 tabindex
设置为“0”可确保如果用户从组件切换到其他位置再返回,组内的选中项将保留焦点。请注意,将 tabindex
更新为“0”也需要将先前选中的项更新为 tabindex="-1"
。此技术涉及以编程方式响应按键事件移动焦点并更新 tabindex
以反映当前聚焦的项。为此
为组中的每个元素绑定一个键盘按下处理程序,当使用箭头键移动到另一个元素时
- 以编程方式将焦点应用于新元素,
- 将聚焦元素的
tabindex
更新为“0”,以及 - 将先前聚焦元素的
tabindex
更新为“-1”。
这是一个使用此技术的WAI-ARIA 树视图示例。
提示
使用element.focus()设置焦点
不要使用createEvent()
、initEvent()
和dispatchEvent()
将焦点发送到元素。DOM 焦点事件仅被视为信息性事件:在某个元素获得焦点后由系统生成,但实际上不用于设置焦点。请改用element.focus()
。
使用onfocus跟踪当前焦点
不要假设所有焦点更改都来自键盘和鼠标事件:屏幕阅读器等辅助技术可以将焦点设置为任何可聚焦元素。请改用onfocus
和onblur
跟踪焦点。
onfocus
和onblur
现在可以与每个元素一起使用。没有标准的DOM接口来获取当前文档焦点。如果要跟踪焦点状态,可以使用document.activeElement获取活动元素。您还可以使用document.hasFocus来确保当前文档焦点。
技术 2:aria-activedescendant
此技术涉及将单个事件处理程序绑定到容器小部件,并使用aria-activedescendant
跟踪“虚拟”焦点。(有关 ARIA 的更多信息,请参阅此可访问 Web 应用程序和小部件概述。)
aria-activedescendant
属性标识当前具有虚拟焦点的后代元素的 ID。容器上的事件处理程序必须响应键和鼠标事件,方法是更新aria-activedescendant
的值并确保当前项目以适当的方式设置样式(例如,使用边框或背景颜色)。
通用指南
使用onkeydown捕获键盘事件,而不是onkeypress
IE 不会为非字母数字键触发keypress
事件。请改用onkeydown
。
确保键盘和鼠标产生相同的体验
为了确保用户体验在任何输入设备下都保持一致,键盘和鼠标事件处理程序应在适当的情况下共享代码。例如,当用户使用箭头键导航时更新tabindex
或样式的代码也应由鼠标单击处理程序使用以产生相同的更改。
确保可以使用键盘激活元素
为了确保可以使用键盘激活元素,绑定到鼠标事件的任何处理程序也应绑定到键盘事件。例如,为了确保 Enter 键可以激活元素,如果您有onclick="doSomething()"
,则还应将doSomething()
绑定到键盘按下事件:onkeydown="event.code === "Enter" && doSomething();"
。
始终为tabindex="-1"的项目和以编程方式接收焦点的元素绘制焦点
IE 不会自动为以编程方式接收焦点的项目绘制焦点轮廓。在更改背景颜色(例如this.style.backgroundColor = "gray";
)或添加点状边框(例如this.style.border = "1px dotted invert"
)之间进行选择。在点状边框的情况下,您需要确保这些元素最初具有不可见的 1px 边框,以便在应用边框样式时元素不会增长(边框占用空间,而 IE 没有实现 CSS 轮廓)。
防止使用的键盘事件执行浏览器功能
如果您的窗口小部件处理键盘事件,请使用事件处理程序的返回值阻止浏览器也处理它(例如,响应箭头键滚动)。如果您的事件处理程序返回false
,则该事件将不会传播到您的处理程序之外。
例如
<span tabindex="-1" onkeydown="return handleKeyDown();">…</span>
如果handleKeyDown()
返回false
,则该事件将被消耗,阻止浏览器根据按键执行任何操作。
目前不要依赖于按键重复的一致行为
不幸的是,onkeydown
可能会或可能不会重复,具体取决于您正在运行的浏览器和操作系统。