WinddSnow

Accessibility-Basics-ARIA-Keyboard-Navigation

字数统计: 4.3k阅读时长: 18 min
2026/04/15

第10课:可访问性(a11y)基础——ARIA、键盘导航与屏幕阅读器

可访问性(Accessibility,常缩写为 a11y,因首尾字母间有 11 个字母)是指让网站或应用对所有用户——包括残障人士——可感知、可操作、可理解、稳健(WCAG 四大原则)。这不仅是道德和法律(如 ADA、Section 508)的要求,更直接扩大用户群体、提升 SEO、改善整体代码质量。本节课将深入讲解可访问性的核心支柱:语义 HTML 的基础作用ARIA 属性的正确用法键盘导航的实现以及屏幕阅读器兼容性


1. 可访问性的基石:语义化 HTML

在讨论 ARIA 之前,必须明确一个最高原则:好的可访问性始于正确的 HTML 语义

1.1 原生语义的自动可访问性

浏览器和辅助技术(如屏幕阅读器)对标准 HTML 元素内置了完整的可访问性支持。例如:

  • <button> 自动被识别为“按钮”,可通过键盘 EnterSpace 触发,并暴露 role="button"
  • <a href="..."> 自动被识别为“链接”,可被 Tab 聚焦,屏幕阅读器会读出“链接”。
  • <input type="checkbox"> 自动暴露 role="checkbox"aria-checked 状态。

因此,优先使用原生语义元素是最高效、最可靠的可访问性策略。

1
2
3
4
5
<!-- ✅ 最佳:原生按钮 -->
<button type="button" onclick="handleClick()">提交</button>

<!-- ❌ 糟糕:用 div 模拟按钮,需要大量 ARIA 修补 -->
<div onclick="handleClick()" tabindex="0" role="button" onkeydown="...">提交</div>

1.2 标题层级(Heading Level)的可访问性意义

屏幕阅读器用户通常通过标题导航(按 H 键)来浏览页面结构。逻辑清晰、无跳跃的标题层级<h1><h2><h3>)是用户理解页面信息架构的关键。

1
2
3
4
5
6
7
8
9
10
11
12
<!-- ✅ 良好的标题结构 -->
<h1>产品介绍</h1>
<h2>功能特性</h2>
<h3>实时协作</h3>
<h3>版本回溯</h3>
<h2>定价方案</h2>
<h3>个人版</h3>
<h3>企业版</h3>

<!-- ❌ 错误的跳跃:从 h1 直接跳到 h4 -->
<h1>产品介绍</h1>
<h4>功能特性</h4>

1.3 替代文本(Alt Text)与可感知性

所有非文本内容(图片、图标、图表)必须提供替代文本,使视障用户能通过屏幕阅读器获取信息。

1
2
3
4
5
6
7
8
<!-- ✅ 描述性替代文本 -->
<img src="sales-chart.png" alt="2026年第一季度销售额柱状图,云计算业务增长35%">

<!-- ✅ 装饰性图片:空 alt,屏幕阅读器会跳过 -->
<img src="decorative-line.png" alt="">

<!-- ❌ 无 alt 属性:阅读器可能读出文件名,造成噪音 -->
<img src="logo.png">

2. ARIA:当 HTML 语义不够用时

ARIA(Accessible Rich Internet Applications)是一套属性,用于补充和增强 HTML 元素的可访问性语义。它不改变元素的行为或外观,仅影响辅助技术(如屏幕阅读器)对元素的解释。

核心警告No ARIA is better than Bad ARIA. 滥用或错误使用 ARIA 比不用更糟糕。

2.1 ARIA 三大类属性

类别 作用 常见属性
角色(Roles) 定义元素的类型(如按钮、对话框、标签页)。 role="button", role="dialog", role="tablist"
状态(States) 描述元素当前的动态状态(屏幕阅读器会在状态变化时自动播报)。 aria-expanded, aria-checked, aria-selected
属性(Properties) 提供元素的额外描述信息(如标签、描述、层级关系)。 aria-label, aria-labelledby, aria-describedby

2.2 最常用的 ARIA 属性详解

aria-labelaria-labelledby:为元素提供可访问名称

当元素没有可见文本标签,或可见文本不足以描述其功能时使用。

1
2
3
4
5
6
7
8
9
10
<!-- 仅有图标没有文字的关闭按钮 -->
<button aria-label="关闭对话框">
<svg>...</svg>
</button>

<!-- 使用现有元素的文本作为标签(多用于对话框标题) -->
<h2 id="dialog-title">确认删除</h2>
<div role="dialog" aria-labelledby="dialog-title">
<p>此操作不可撤销,确定删除吗?</p>
</div>

aria-describedby:提供补充描述信息

关联一个或多个元素的 id,为控件提供额外的说明文字。

1
2
3
<label for="pwd">密码:</label>
<input type="password" id="pwd" aria-describedby="pwd-hint">
<p id="pwd-hint">密码至少 8 位,包含字母和数字。</p>

屏幕阅读器聚焦输入框时会读出:“密码,编辑文本,密码至少 8 位,包含字母和数字。”

aria-expanded:指示可展开控件的状态

用于菜单、下拉框、手风琴等可展开/收起的控件。

1
2
3
4
5
<button aria-expanded="false" aria-controls="menu-list">菜单</button>
<ul id="menu-list" hidden>
<li><a href="#">选项一</a></li>
<li><a href="#">选项二</a></li>
</ul>

当 JavaScript 展开菜单时,需同步更新 aria-expanded="true" 并移除 hidden

aria-hidden:对辅助技术隐藏元素

值为 true 时,该元素及其子元素对屏幕阅读器完全不可见。用于隐藏纯装饰性内容或重复内容(如重复的图标字体)。

1
2
<!-- 装饰性图标,不需要被读出 -->
<span aria-hidden="true">🎨</span> 设计

aria-live:宣布动态内容更新

用于通知屏幕阅读器某区域内容已更新(如聊天消息、表单错误、股票价格)。无需移动焦点即可自动播报。

aria-live 行为
off 默认。不宣布更新。
polite 在当前播报结束后再宣布更新,不打断用户。
assertive 立即打断当前播报,宣布更新内容。仅用于紧急通知(如错误)
1
2
3
4
<div aria-live="polite" aria-atomic="true">
<!-- 购物车商品数量会动态更新,屏幕阅读器将自动播报 -->
购物车中有 <span id="cart-count">0</span> 件商品。
</div>

aria-atomic="true" 表示播报该区域的完整内容,而非仅变化的部分。

2.3 ARIA 角色(Roles)使用原则

仅当原生 HTML 无法表达所需语义时,才使用 role 属性。

1
2
3
4
5
6
7
8
<!-- 自定义选项卡组件(原生无 tabs 元素) -->
<div role="tablist" aria-label="编程语言选项卡">
<button role="tab" aria-selected="true" aria-controls="panel1" id="tab1">JavaScript</button>
<button role="tab" aria-selected="false" aria-controls="panel2" id="tab2">Python</button>
</div>
<div role="tabpanel" id="panel1" aria-labelledby="tab1">
<p>JavaScript 是一种动态脚本语言...</p>
</div>

常见错误:冗余的角色声明。

1
2
3
4
5
<!-- ❌ 错误:原生 button 已有 role="button",无需重复声明 -->
<button role="button">点击</button>

<!-- ✅ 正确 -->
<button>点击</button>

3. 键盘导航:确保所有功能可通过键盘操作

WCAG 2.1 要求:所有交互功能必须可通过键盘操作(有例外,如依赖路径的手绘)。这是运动障碍用户和重度键盘用户的基本需求。

3.1 焦点管理基础

  • 可聚焦元素<a>(有 href)、<button><input><select><textarea><details><summary>、以及设置了 tabindex 的元素。
  • Tab 键:正向移动焦点。
  • Shift + Tab:反向移动焦点。

3.2 tabindex 的三种用法

tabindex 行为 使用场景
0 元素被加入自然 Tab 顺序(按 DOM 顺序),可通过键盘聚焦。 使非交互元素(如自定义 <div> 按钮)可聚焦。
-1 元素从 Tab 顺序中移除,但仍可通过 JavaScript focus() 方法聚焦。 模态对话框内的初始焦点设置、可聚焦但不想让用户 Tab 到的元素。
> 0 强制指定 Tab 优先级。强烈不推荐,会破坏自然 DOM 顺序,导致焦点跳跃混乱。 几乎无正当场景,应通过调整 DOM 顺序而非修改 tabindex 来优化导航。

3.3 键盘陷阱与模态对话框

当打开模态对话框时,焦点应被限制在对话框内部(按 Tab 不会跑到背景页面),直到用户关闭对话框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 简化的焦点陷阱逻辑
const dialog = document.getElementById('modal');
const focusableElements = dialog.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];

dialog.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
}
});

3.4 跳过导航链接(Skip Link)

为键盘用户提供“跳转到主要内容”的链接,避免每次页面加载都要 Tab 穿过整个导航栏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<a href="#main-content" class="skip-link">跳转到主要内容</a>
<header>...</header>
<nav>...</nav>
<main id="main-content" tabindex="-1">
<!-- 主要内容 -->
</main>
</body>

<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
</style>

注意:目标容器(<main>)需设置 tabindex="-1",使其可被 JavaScript 聚焦,但不进入 Tab 顺序。

3.5 焦点可见性::focus-visible

不要移除焦点轮廓(outline: none)除非提供了更明显的替代样式。使用 :focus-visible 可为键盘导航保留焦点指示,同时避免鼠标点击时出现不必要的轮廓。

1
2
3
4
5
6
7
8
9
10
/* 仅当通过键盘聚焦时显示轮廓 */
:focus-visible {
outline: 3px solid #0066cc;
outline-offset: 2px;
}

/* 鼠标点击时无轮廓(浏览器默认行为) */
button:focus:not(:focus-visible) {
outline: none;
}

4. 颜色对比度与可感知性

WCAG 2.1 AA 级要求:

  • 普通文本:对比度至少 4.5:1
  • 大文本(18pt 或 14pt 加粗):对比度至少 3:1
  • UI 组件与图形:对比度至少 3:1

可使用浏览器开发者工具(如 Chrome DevTools 的 CSS Overview)或在线工具(如 WebAIM Contrast Checker)检测对比度。

1
2
3
4
5
/* ❌ 对比度不足:浅灰文字在白色背景上 */
p { color: #999; background: #fff; } /* 对比度仅 2.3:1 */

/* ✅ 满足 AA 标准 */
p { color: #595959; background: #fff; } /* 对比度 4.5:1 */

注意颜色不能作为传达信息的唯一手段。例如,错误提示不能仅用红色边框表示,还需配合图标和文字说明。

1
2
3
4
5
6
7
8
<!-- ❌ 仅靠颜色区分 -->
<input type="text" class="error" aria-invalid="true">

<!-- ✅ 颜色 + 文字 + 图标 -->
<div class="error-message">
<span aria-hidden="true">⚠️</span> 用户名不能为空
</div>
<input type="text" aria-invalid="true" aria-describedby="username-error">

5. 屏幕阅读器测试与工具

5.1 常用屏幕阅读器

操作系统 屏幕阅读器 启动方式
Windows NVDA(免费) Ctrl + Alt + N
Windows JAWS(付费) 桌面图标
macOS VoiceOver Cmd + F5
iOS VoiceOver 设置 → 辅助功能
Android TalkBack 设置 → 辅助功能

5.2 浏览器开发者工具辅助

  • Chrome DevTools:Lighthouse 审计(可自动检测对比度、ARIA 错误、标题层级等)。
  • Firefox DevTools:可访问性检查器(显示元素的可访问名称、角色、状态)。
  • axe DevTools 扩展:行业标准的自动化可访问性测试工具。

课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) 以下哪个属性用于为屏幕阅读器提供元素的补充描述信息,而不会覆盖其可访问名称?
    A. aria-label
    B. aria-labelledby
    C. aria-describedby
    D. aria-hidden

  2. (单选) 关于 tabindex 的使用,下列哪项是正确的?
    A. 使用 tabindex="1" 可让元素成为 Tab 顺序的第一个。
    B. 使用 tabindex="0" 可将非交互元素加入自然 Tab 顺序。
    C. tabindex="-1" 的元素无法通过任何方式获得焦点。
    D. 应优先使用正数 tabindex 来优化键盘导航。

  3. (填空) 要通知屏幕阅读器某区域内容已更新,且不打断当前播报,应设置 aria-live="______"

  4. (多选) 以下哪些措施有助于提升键盘导航的可访问性?
    A. 提供“跳转到主要内容”链接。
    B. 在模态对话框打开时将焦点限制在对话框内。
    C. 使用 CSS outline: none 移除所有焦点轮廓。
    D. 确保所有交互元素可通过 Tab 键聚焦。

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要实现一个可访问的“更多操作”下拉菜单组件。要求如下:

  • 触发按钮显示文字“操作”和一个向下箭头图标。
  • 菜单默认隐藏,点击按钮后展开,再次点击或点击外部区域时收起。
  • 必须支持键盘操作:EnterSpace 展开/收起;展开后 Tab 可进入菜单项;Esc 关闭菜单并将焦点返回按钮。
  • 使用正确的 ARIA 属性:aria-expandedaria-controlsrole="menu"role="menuitem"
  • 箭头图标应设为 aria-hidden="true",并为按钮提供明确的 aria-label

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的 HTML、CSS 和 JavaScript 代码。提示词中应明确指定可访问性要求(ARIA 属性、键盘交互、焦点管理)。

三、面试真题与参考答案

题目(腾讯前端面试题):

请解释 ARIA 属性 aria-labelaria-labelledbyaria-describedby 的区别。在一个具有可见文本“删除”的按钮上,同时存在 SVG 图标,应如何使用这些属性以确保最佳可访问性?

参考答案

1. 三者区别

属性 作用 是否覆盖可访问名称 典型场景
aria-label 直接提供一个字符串作为元素的可访问名称。 元素无可见文本,或可见文本不足以描述功能。
aria-labelledby 通过空格分隔的 id 列表,引用其他元素的内容作为可访问名称。优先级高于 aria-label 和原生标签。 多个元素共同描述控件(如对话框标题 + 副标题),或标签已在别处存在。
aria-describedby 引用其他元素的内容作为补充描述(不覆盖可访问名称)。阅读器在播报名称后,会追加播报描述内容。 提供帮助文本、格式提示、错误信息。

2. 按钮场景应用

对于既有图标又有可见文字“删除”的按钮,最佳实践是让可见文字本身就作为可访问名称,无需额外 ARIA 属性。

1
2
3
4
<button>
<svg aria-hidden="true" focusable="false">...</svg>
删除
</button>
  • 图标设置 aria-hidden="true" 防止被屏幕阅读器读出(避免重复)。
  • 按钮内的“删除”文本自动成为可访问名称。
  • 若图标后无可见文字,则需使用 aria-label="删除"

3. 错误用法警示

1
2
3
4
5
<!-- ❌ 冗余:aria-label 与可见文字重复,且可能导致阅读器读出两遍 -->
<button aria-label="删除">
<svg aria-hidden="true">...</svg>
删除
</button>

课后练习答案

一、概念自测答案

  1. C

    • 解析:aria-describedby 提供补充描述,不覆盖名称。aria-labelaria-labelledby 会设置可访问名称。
  2. B

    • 解析:tabindex="0" 将元素加入自然 Tab 顺序。A 错误,正数 tabindex 破坏顺序;C 错误,-1 可通过脚本聚焦;D 错误,应避免正数 tabindex
  3. polite

    • 解析:aria-live="polite" 在用户空闲时播报,不打断当前操作。
  4. A、B、D

    • 解析:C 错误,移除焦点轮廓会严重损害键盘用户体验,应使用 :focus-visible 提供替代样式。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请实现一个可访问的下拉菜单组件(HTML + CSS + JavaScript)。要求:

  • 触发按钮显示文字 操作 和一个向下箭头图标(可用 SVG 或 Unicode 字符)。
  • 菜单初始隐藏,点击按钮切换展开/收起;点击外部区域自动收起。
  • 键盘支持:EnterSpace 切换菜单;Escape 关闭菜单并聚焦回按钮;菜单展开时按 Tab 可在菜单项间导航,Shift+Tab 返回按钮。
  • 正确使用 ARIA:按钮设置 aria-expandedaria-controls;菜单容器设置 role="menu";菜单项设置 role="menuitem";箭头图标 aria-hidden="true";若按钮无可见文本则提供 aria-label
  • 使用 CSS :focus-visible 管理焦点样式,不直接移除 outline
  • 代码应有清晰注释。请输出完整可运行示例。”
CATALOG
  1. 1. 第10课:可访问性(a11y)基础——ARIA、键盘导航与屏幕阅读器
    1. 1.1. 1. 可访问性的基石:语义化 HTML
      1. 1.1.1. 1.1 原生语义的自动可访问性
      2. 1.1.2. 1.2 标题层级(Heading Level)的可访问性意义
      3. 1.1.3. 1.3 替代文本(Alt Text)与可感知性
    2. 1.2. 2. ARIA:当 HTML 语义不够用时
      1. 1.2.1. 2.1 ARIA 三大类属性
      2. 1.2.2. 2.2 最常用的 ARIA 属性详解
      3. 1.2.3. 2.3 ARIA 角色(Roles)使用原则
    3. 1.3. 3. 键盘导航:确保所有功能可通过键盘操作
      1. 1.3.1. 3.1 焦点管理基础
      2. 1.3.2. 3.2 tabindex 的三种用法
      3. 1.3.3. 3.3 键盘陷阱与模态对话框
      4. 1.3.4. 3.4 跳过导航链接(Skip Link)
      5. 1.3.5. 3.5 焦点可见性::focus-visible
    4. 1.4. 4. 颜色对比度与可感知性
    5. 1.5. 5. 屏幕阅读器测试与工具
      1. 1.5.1. 5.1 常用屏幕阅读器
      2. 1.5.2. 5.2 浏览器开发者工具辅助
    6. 1.6. 课后练习
      1. 1.6.1. 一、概念自测(选择题 / 填空题)
      2. 1.6.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.6.3. 三、面试真题与参考答案
    7. 1.7. 课后练习答案
      1. 1.7.1. 一、概念自测答案
      2. 1.7.2. 二、AI 编程任务参考答案(提示词示例)