用途
本技能是一份使用屏幕阅读器测试网页应用的实用指南,面向需要做全面可访问性验证的场景,帮助开发者与测试人员发现辅助技术下的可用性问题。
何时使用
- 验证屏幕阅读器兼容性
- 测试 ARIA 实现
- 调试辅助技术问题
- 验证表单可访问性
- 测试动态内容播报
- 确保导航可访问性
核心概念
主要屏幕阅读器
资料给出以下对照:
- VoiceOver:macOS/iOS,浏览器 Safari,使用占比约 15%
- NVDA:Windows,浏览器 Firefox/Chrome,使用占比约 31%
- JAWS:Windows,浏览器 Chrome/IE,使用占比约 40%
- TalkBack:Android,浏览器 Chrome,使用占比约 10%
- Narrator:Windows,浏览器 Edge,使用占比约 4%
测试优先级
最低覆盖:
- NVDA + Firefox(Windows)
- VoiceOver + Safari(macOS)
- VoiceOver + Safari(iOS)
全面覆盖:
- 加上 JAWS + Chrome(Windows)
- 加上 TalkBack + Chrome(Android)
- 加上 Narrator + Edge(Windows)
屏幕阅读器模式
- 浏览/虚拟模式:用于阅读内容,默认阅读状态
- 焦点/表单模式:用于与控件交互,填写表单时使用
- 应用模式:用于自定义组件,ARIA 应用
各平台准备
- 在 macOS 测试前,阅读 VoiceOver 的设置、命令和检查清单
- 对于必需的 iOS Safari 覆盖,阅读 VoiceOver 触控设置和手势
- 使用 NVDA 测试前,阅读 NVDA 设置、模式、命令和测试脚本
- 使用 JAWS 或 TalkBack 测试前,阅读对应平台的设置和命令
常见测试场景
1. 模态对话框
资料给出可访问的模态结构示例:
<!-- Accessible modal structure -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
>
<h2 id="dialog-title">Confirm Delete</h2>
<p id="dialog-desc">This action cannot be undone.</p>
<button>Cancel</button>
<button>Delete</button>
</div>
焦点管理代码:
// Focus management
function openModal(modal) {
// Store last focused element
lastFocus = document.activeElement;
// Move focus to modal
modal.querySelector("h2").focus();
// Trap focus
modal.addEventListener("keydown", trapFocus);
}
function closeModal(modal) {
// Return focus
lastFocus.focus();
}
function trapFocus(e) {
if (e.key === "Tab") {
const focusable = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
last.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === last) {
first.focus();
e.preventDefault();
}
}
if (e.key === "Escape") {
closeModal(modal);
}
}
2. 实时区域
<!-- Status messages (polite) -->
<div role="status" aria-live="polite" aria-atomic="true">
<!-- Content updates will be announced after current speech -->
</div>
<!-- Alerts (assertive) -->
<div role="alert" aria-live="assertive">
<!-- Content updates interrupt current speech -->
</div>
<!-- Progress updates -->
<div
role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress"
></div>
<!-- Log (additions only) -->
<div role="log" aria-live="polite" aria-relevant="additions">
<!-- New messages announced, removals not -->
</div>
3. 标签页界面
<div role="tablist" aria-label="Product information">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
Description
</button>
<button
role="tab"
id="tab-2"
aria-selected="false"
aria-controls="panel-2"
tabindex="-1"
>
Reviews
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Product description content...
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
Reviews content...
</div>
键盘导航代码:
// Tab keyboard navigation
tablist.addEventListener("keydown", (e) => {
const tabs = [...tablist.querySelectorAll('[role="tab"]')];
const index = tabs.indexOf(document.activeElement);
let newIndex;
switch (e.key) {
case "ArrowRight":
newIndex = (index + 1) % tabs.length;
break;
case "ArrowLeft":
newIndex = (index - 1 + tabs.length) % tabs.length;
break;
case "Home":
newIndex = 0;
break;
case "End":
newIndex = tabs.length - 1;
break;
default:
return;
}
tabs[newIndex].focus();
activateTab(tabs[newIndex]);
e.preventDefault();
});
调试技巧
资料给出记录屏幕阅读器所“看到”内容的函数:
// Log what screen reader sees
function logAccessibleName(element) {
const computed = window.getComputedStyle(element);
console.log({
role: element.getAttribute("role") || element.tagName,
name:
element.getAttribute("aria-label") ||
element.getAttribute("aria-labelledby") ||
element.textContent,
state: {
expanded: element.getAttribute("aria-expanded"),
selected: element.getAttribute("aria-selected"),
checked: element.getAttribute("aria-checked"),
disabled: element.disabled,
},
visible: computed.display !== "none" && computed.visibility !== "hidden",
});
}
最佳实践
应当做:
- 使用真实屏幕阅读器测试,而不只是模拟器
- 优先使用语义化 HTML,ARIA 作为补充
- 在浏览模式和焦点模式下都测试,两者体验不同
- 验证焦点管理,尤其是单页应用(SPA)
- 先做纯键盘测试,这是屏幕阅读器测试的基础
不应当做:
- 不要以为一款屏幕阅读器就够了,要多款测试
- 不要忽略移动端,用户群体在增长
- 不要只测顺利路径,要测错误状态
- 不要跳过动态内容,这是最常见的问题来源
- 不要依赖视觉测试,屏幕阅读器体验不同
限制
资料未提供该技能的具体安装命令、运行环境要求、收费方式或与具体工具链的集成说明,这些信息暂未提供。