外观
键盘无障碍
什么是键盘无障碍
键盘无障碍是指所有网站功能和交互都能通过键盘独立完成,不依赖鼠标或其他指点设备。这是 Web 无障碍的核心要求,服务于运动障碍用户、盲人用户和高效键盘用户。
示意图:
鼠标用户: 点击按钮 → 执行操作
键盘用户: Tab到按钮 → 按Enter/Space → 执行操作核心原理:键盘导航通过焦点管理系统,让用户按逻辑顺序遍历所有交互元素,使用标准键或自定义快捷键激活功能。
键盘导航基础
焦点与焦点指示器
焦点是键盘导航中当前选中的界面元素,焦点指示器是显示焦点位置的视觉反馈。
代码示例:
html
<button class="focused">提交</button>CSS 焦点样式:
css
/* 默认焦点样式 */
button:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* 高对比度支持 */
@media (prefers-contrast: high) {
button:focus {
outline: 3px solid #000000;
}
}
/* 自定义焦点样式 */
.custom-button:focus {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
border-color: #005fcc;
}示意图:
焦点移动: [按钮1] → Tab → [按钮2] (带焦点环) → Tab → [链接1]
视觉反馈: 蓝色轮廓环围绕当前焦点元素标准键盘导航模式
不同操作系统和浏览器的键盘导航行为基本一致:
Tab键: 向前移动到下一个可聚焦元素
Shift+Tab: 向后移动到上一个可聚焦元素
Enter/Space: 激活按钮、链接等元素
箭头键: 在组件内导航(单选按钮、菜单等)
Esc: 关闭模态框、弹出层焦点管理原理
焦点顺序
焦点顺序由 DOM 顺序和 tabindex 属性共同决定。
代码示例:
html
<button tabindex="0">按钮A</button>
<div tabindex="0">自定义焦点元素</div>
<button tabindex="1">优先焦点按钮</button>
<button tabindex="0">按钮B</button>
<button tabindex="-1">编程焦点元素</button>焦点顺序示意图:
tabindex="1" → tabindex="0" (按DOM顺序) → 跳过tabindex="-1"
按钮顺序: [优先焦点按钮] → [按钮A] → [自定义焦点元素] → [按钮B]tabindex 属性详解
html
<!-- 自然焦点顺序 (默认) -->
<button>按钮</button>
<!-- tabindex="0" 隐式 -->
<!-- 显式包含在焦点顺序 -->
<div tabindex="0">可聚焦div</div>
<!-- 优先焦点 (避免使用) -->
<span tabindex="1">不推荐</span>
<!-- 编程焦点,不在自然顺序 -->
<div tabindex="-1" id="programmatic-focus">编程控制</div>JavaScript 焦点控制:
javascript
// 设置焦点
document.getElementById('programmatic-focus').focus()
// 焦点陷阱(模态框内)
function trapFocus(modalElement) {
const focusableElements = modalElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
)
const firstElement = focusableElements[0]
const lastElement = focusableElements[focusableElements.length - 1]
modalElement.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
// Shift + Tab
if (document.activeElement === firstElement) {
lastElement.focus()
e.preventDefault()
}
} else {
// Tab
if (document.activeElement === lastElement) {
firstElement.focus()
e.preventDefault()
}
}
}
})
}原生 HTML 元素的键盘支持
表单控件
不同表单元素有特定的键盘交互模式。
代码示例:
html
<!-- 文本输入 -->
<input type="text" placeholder="输入姓名" />
<!-- 单选按钮组 -->
<fieldset>
<legend>选择支付方式</legend>
<input type="radio" id="credit" name="payment" value="credit" />
<label for="credit">信用卡</label>
<input type="radio" id="paypal" name="payment" value="paypal" />
<label for="paypal">PayPal</label>
</fieldset>
<!-- 下拉选择 -->
<select>
<option value="1">选项一</option>
<option value="2">选项二</option>
</select>键盘交互示意图:
文本输入: 直接输入文字
单选按钮: ↓/→ 下一个选项, ↑/← 上一个选项
下拉菜单: ↓ 打开, ↑/↓ 选择选项, Enter 确认交互元素
按钮、链接等基础交互元素的键盘行为。
代码示例:
html
<!-- 按钮 -->
<button onclick="submitForm()">提交</button>
<!-- 链接 -->
<a href="/about">关于我们</a>
<!-- 详情折叠 -->
<details>
<summary>更多信息</summary>
<p>详细内容...</p>
</details>键盘行为:
按钮: Enter/Space 激活
链接: Enter 跳转
详情折叠: Enter/Space 切换展开状态自定义组件的键盘无障碍
自定义按钮
使用 JavaScript 增强自定义元素的键盘交互。
代码示例:
html
<div
class="custom-button"
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="handleKeydown(event)"
>
自定义按钮
</div>JavaScript 实现:
javascript
function handleKeydown(event) {
// 支持Enter和Space激活
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault() // 防止Space滚动页面
handleClick()
}
}
function handleClick() {
// 按钮点击逻辑
console.log('按钮被激活')
}自定义下拉菜单
实现完整的键盘导航模式。
代码示例:
html
<div class="custom-select">
<button
id="select-button"
aria-haspopup="listbox"
aria-expanded="false"
onkeydown="handleSelectKeydown(event)"
>
选择选项
</button>
<ul
id="select-list"
role="listbox"
aria-labelledby="select-button"
hidden
>
<li role="option" tabindex="-1">选项一</li>
<li role="option" tabindex="-1">选项二</li>
<li role="option" tabindex="-1">选项三</li>
</ul>
</div>JavaScript 实现:
javascript
function handleSelectKeydown(event) {
const list = document.getElementById('select-list')
const options = list.querySelectorAll('[role="option"]')
switch (event.key) {
case 'Enter':
case ' ':
case 'ArrowDown':
event.preventDefault()
list.hidden = false
options[0].focus()
break
case 'ArrowUp':
event.preventDefault()
list.hidden = false
options[options.length - 1].focus()
break
}
}
// 选项键盘处理
function handleOptionKeydown(event, index) {
const options = document.querySelectorAll('[role="option"]')
switch (event.key) {
case 'ArrowDown':
event.preventDefault()
options[(index + 1) % options.length].focus()
break
case 'ArrowUp':
event.preventDefault()
options[(index - 1 + options.length) % options.length].focus()
break
case 'Enter':
case ' ':
event.preventDefault()
selectOption(event.target)
break
case 'Escape':
event.preventDefault()
closeDropdown()
break
}
}复杂组件的键盘模式
模态对话框
模态框需要焦点管理和焦点陷阱。
代码示例:
html
<div
id="modal"
role="dialog"
aria-labelledby="modal-title"
aria-modal="true"
>
<h2 id="modal-title">确认操作</h2>
<p>您确定要执行此操作吗?</p>
<div>
<button onclick="confirmAction()">确认</button>
<button onclick="closeModal()">取消</button>
</div>
</div>JavaScript 焦点管理:
javascript
let previousActiveElement
function openModal() {
const modal = document.getElementById('modal')
previousActiveElement = document.activeElement
// 显示模态框
modal.style.display = 'block'
// 焦点陷阱
trapFocus(modal)
// 初始焦点
const firstFocusable = modal.querySelector('button')
firstFocusable.focus()
}
function closeModal() {
const modal = document.getElementById('modal')
modal.style.display = 'none'
// 恢复焦点
if (previousActiveElement) {
previousActiveElement.focus()
}
}标签页组件
实现箭头键导航和 Home/End 支持。
代码示例:
html
<div role="tablist" aria-label="内容标签">
<button
role="tab"
aria-selected="true"
aria-controls="panel1"
id="tab1"
onkeydown="handleTabKeydown(event, 0)"
>
标签一
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel2"
id="tab2"
onkeydown="handleTabKeydown(event, 1)"
>
标签二
</button>
</div>
<div role="tabpanel" id="panel1" aria-labelledby="tab1">标签一内容</div>
<div role="tabpanel" id="panel2" aria-labelledby="tab2" hidden>
标签二内容
</div>键盘处理函数:
javascript
function handleTabKeydown(event, index) {
const tabs = document.querySelectorAll('[role="tab"]')
switch (event.key) {
case 'ArrowRight':
event.preventDefault()
tabs[(index + 1) % tabs.length].focus()
break
case 'ArrowLeft':
event.preventDefault()
tabs[(index - 1 + tabs.length) % tabs.length].focus()
break
case 'Home':
event.preventDefault()
tabs[0].focus()
break
case 'End':
event.preventDefault()
tabs[tabs.length - 1].focus()
break
case 'Enter':
case ' ':
event.preventDefault()
activateTab(index)
break
}
}高级键盘导航特性
跳过链接
为键盘用户提供跳过重复导航的快捷方式。
代码示例:
html
<a href="#main-content" class="skip-link">跳到主内容</a>
<nav>...</nav>
<main id="main-content">
<!-- 页面主要内容 -->
</main>CSS 实现:
css
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
}
.skip-link:focus {
top: 6px;
}键盘快捷键
实现自定义快捷键,避免与浏览器和屏幕阅读器冲突。
代码示例:
javascript
document.addEventListener('keydown', (event) => {
// Ctrl+K 聚焦搜索框 (常见模式)
if (event.ctrlKey && event.key === 'k') {
event.preventDefault()
document.getElementById('search-input').focus()
}
// ? 打开帮助
if (event.key === '?' && !event.ctrlKey && !event.altKey) {
event.preventDefault()
openHelpDialog()
}
})ARIA 标注快捷键:
html
<button aria-keyshortcuts="Shift+S" onclick="openSettings()">设置</button>测试键盘无障碍
手动测试流程
完整的键盘测试检查清单:
1. Tab导航测试
- 按Tab向前移动焦点
- 按Shift+Tab向后移动焦点
- 检查焦点顺序是否合理
2. 焦点指示器测试
- 每个焦点元素都有可见焦点指示器
- 焦点环不遮挡内容
3. 组件交互测试
- 按钮: Enter/Space激活
- 菜单: 箭头键导航
- 模态框: Esc关闭,焦点陷阱
4. 跳过链接测试
- 跳过链接在焦点时可见
- 正确跳转到目标内容
5. 快捷键测试
- 自定义快捷键正常工作
- 不冲突于浏览器快捷键自动化测试
使用 JavaScript 检测键盘无障碍问题。
代码示例:
javascript
// 检查所有交互元素是否可键盘访问
function testKeyboardAccessibility() {
const interactiveSelectors = [
'button:not([disabled])',
'a[href]',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
]
const interactiveElements = document.querySelectorAll(
interactiveSelectors.join(','),
)
interactiveElements.forEach((element) => {
const style = window.getComputedStyle(element)
// 检查焦点样式
if (style.outlineStyle === 'none' && style.outlineWidth === '0px') {
console.warn('元素缺少可见焦点样式:', element)
}
// 检查尺寸(运动障碍用户需要足够大的点击目标)
const rect = element.getBoundingClientRect()
if (rect.width < 44 || rect.height < 44) {
console.warn('交互元素尺寸过小:', element)
}
})
}开发者工具测试
使用浏览器开发者工具验证焦点顺序:
Chrome DevTools:
1. 打开Elements面板
2. 运行 document.activeElement 查看当前焦点
3. 使用 Accessibility面板检查焦点树
Firefox Accessibility Inspector:
1. 打开Accessibility面板
2. 查看可访问性树中的焦点顺序
3. 检查键盘属性