欢迎光临
我们一直在努力

跟着 MDN 学无障碍 Day 2:解决常见的无障碍问题——从 HTML 到实战测试

前言:无障碍不是玄学,而是可诊断可修复的问题

在上一篇文章中,我们建立了对无障碍的基本认知,了解了视觉障碍、听觉障碍、行动障碍和认知障碍用户如何访问网页,以及为什么无障碍设计对所有人都有益。然而,认知只是第一步。在实际开发中,我们面对的是具体的代码和具体的用户场景。无障碍问题不是抽象的概念,而是可以通过系统化的方法进行诊断、测试和修复的。

本文将深入探讨 Web 无障碍中最常见的问题类型,涵盖 HTML、CSS 和 JavaScript 三个技术层面,并提供具体的测试方法和修复方案。我们将学习如何通过键盘测试、屏幕阅读器测试、审计工具等多种手段,来验证我们的网站是否真正做到了可访问。

需要明确的是,无障碍并非一个可以用 100% 成功或失败来定义的目标。随着网站变得越来越复杂,对所有内容实现完全的无障碍几乎是不可能的。但这不应该成为放弃的理由。我们所做的,更多是采取防御性编程策略,遵循最佳实践,确保尽可能多的人能够访问到尽可能多的内容。这是一个持续改进的过程,而非一次性的检查清单。

HTML 语义化:无障碍的第一道防线

HTML 语义化是构建可访问网站的基石。所谓语义化,就是使用正确的 HTML 标签来标记内容,让每个元素都能准确传达其结构和含义。这不仅让使用屏幕阅读器的用户能够理解页面结构,也为搜索引擎和其他自动化工具提供了清晰的上下文信息。

来看一个典型的对比示例。以下是两种创建标题和段落的方式:

<!– 不语义化的写法 –>
<font size="7">我的标题</font>
<br /><br />
这是我文档的第一部分。
<br /><br />
我还会在这里添加另一个段落。
<br /><br />
<font size="5">我的子标题</font>
<br /><br />
这是我文档的第一个子部分。我希望人们能够找到这些内容!
<br /><br />
<font size="5">我的第二个子标题</font>
<br /><br />
这是我内容的第二个子部分。我认为这比上一个更有趣。

这种写法的问题在于,屏幕阅读器完全无法识别哪些内容是标题、哪些是段落。用户面对的是连续不断的一堆文本,没有任何结构标识,他们无法快速跳转到感兴趣的部分,只能从头到尾听完所有内容。而使用语义化标签后,情况完全不同:

<h1>我的标题</h1>

<p>这是我文档的第一部分。</p>

<p>我还会在这里添加另一个段落。</p>

<h2>我的子标题</h2>

<p>这是我文档的第一个子部分。我希望人们能够找到这些内容!</p>

<h2>我的第二个子标题</h2>

<p>这是我内容的第二个子部分。我认为这比上一个更有趣。</p>

在这个版本中,h1 标签明确标识了主标题,h2 标签标识了子标题,p 标签标识了段落。屏幕阅读器用户可以按标题层级快速导航,比如直接跳转到第二个子标题。这种导航能力对于浏览长文档的用户来说至关重要,就像视力正常的用户可以通过视觉扫读来快速定位信息一样。

一个实用的测试方法是关闭网页的 CSS 样式,然后检查内容是否仍然清晰可读。不同浏览器的操作方式略有不同:在 Firefox 中选择"查看"菜单下的"页面样式"然后选择"无样式";在 Safari 中需要先在偏好设置中开启开发菜单,然后选择"开发"菜单下的"停用样式";在 Chrome 中可以安装 Web Developer Toolbar 扩展来实现此功能;在 Edge 中选择"查看"菜单下的"样式"然后选择"无样式"。如果去除样式后页面内容依然有条理、有层次,说明你的 HTML 结构是合理的。

键盘无障碍:最基础也最容易被忽视

键盘无障碍是指网站的所有功能都可以仅通过键盘来完成操作。这对于行动障碍用户来说是至关重要的,因为他们可能无法使用鼠标。同时,这也惠及那些习惯使用键盘操作的高级用户。

某些 HTML 元素天然支持键盘交互,这是从早期 Web 开始就存在的特性。这些元素包括链接、按钮、以及表单元素等。来看一个简单的测试页面,包含可被键盘聚焦的原生元素:

<a href="#">链接</a>
<button>按钮</button>
<input type="text" placeholder="文本输入框">
<select>
<option>选项一</option>
<option>选项二</option>
</select>

当你在浏览器中打开这个页面并按 Tab 键时,焦点会在这些元素之间依次移动。被聚焦的元素通常会显示一个蓝色的边框或其他视觉标识,让你知道当前焦点在哪个元素上。在 Firefox 中,你还可以启用无障碍检查器来查看页面的 Tab 键导航顺序。按下 Enter 键可以激活链接或按钮,在文本框中可以开始输入,在 select 元素中可以使用上下箭头键来浏览选项。

然而,在开发中经常出现的一个严重问题是:使用非交互元素来模拟交互功能。例如,很多开发者习惯用 div 或 span 来创建按钮:

<!– 这是一种糟糕的做法 –>
<div class="btn" onclick="handleClick()">点击我</div>

虽然这个 div 通过 CSS 可能看起来像一个按钮,通过 JavaScript 也可以响应点击事件,但它无法通过键盘的 Tab 键获得焦点,也无法通过 Enter 键来触发。要修复这个问题,可以给 div 添加 tabindex 属性并手动处理键盘事件:

<!– 仅仅是为了演示这种补丁做法,实际不推荐 –>
<div class="btn" tabindex="0" onclick="handleClick()">点击我</div>

然后在 JavaScript 中添加键盘事件处理:

document.onkeydown = function(e) {
if (e.code === "Enter") {
document.activeElement.onclick(e);
}
};

但这种方式只能处理通过 onclick 属性绑定的事件,对 addEventListener 绑定的监听器无效。这还带来了额外的代码复杂度和维护负担。最正确的做法始终是使用原生的 button 元素,它自带了键盘可访问性,不需要任何额外的代码。

在样式处理上,建议同时为元素的悬停状态和聚焦状态设置样式。这样可以确保无论用户使用鼠标还是键盘,都能获得清晰的视觉反馈:

a:hover, input:hover, button:hover, select:hover,
a:focus, input:focus, button:focus, select:focus
{
font-weight: bold;
}

如果你因为设计原因需要移除浏览器的默认聚焦样式,请务必用自定义的样式来替代。移除聚焦样式而不提供替代方案,会严重损害键盘用户的操作体验。

替代文本:让非文本内容可被感知

对于视觉或听觉障碍的用户来说,任何非文本内容都是一个潜在的障碍。图片、视频、音频等内容都需要提供文字形式的替代方案。

图片的替代文本是最常见也最容易实现的无障碍特性。所有包含有意义信息的图片都应该具有 alt 属性:

<!– 缺乏替代文本 –>
<img src="sales-chart.png">

<!– 提供了有意义的替代文本 –>
<img src="sales-chart.png" alt="2024年各季度销售额柱状图:第一季度120万,第二季度150万,第三季度180万,第四季度210万">

如果图片纯粹是装饰性的,应该将 alt 设置为空字符串,这样屏幕阅读器会直接跳过它,避免干扰用户获取有效信息:

<img src="decorative-line.png" alt="">

对于视频内容,可以使用 track 元素来提供字幕。track 元素使用 WebVTT 格式的字幕文件,可以为不同语言提供不同的字幕轨道:

<video controls>
<source src="tutorial.mp4" type="video/mp4">
<track src="subtitles-zh.vtt" kind="subtitles" srclang="zh" label="中文字幕" default>
<track src="subtitles-en.vtt" kind="subtitles" srclang="en" label="English subtitles">
您的浏览器不支持视频播放,请查看下方的文字记录。
</video>

这里的 kind 属性值 subtitles 表示这是一个字幕轨道,srclang 指定了字幕的语言,label 属性为用户提供了一个可读的选项名称。default 属性将这个轨道设为默认开启。video 标签内的后备文字在浏览器完全不支持视频元素时也能提供信息。

元素关系与上下文:链接、表单与表格

屏幕阅读器用户有一个常用的功能:调出页面上所有链接的列表,以便快速导航。因此,链接文本本身必须在脱离上下文的情况下仍然具有意义。考虑以下两种写法:

<!– 糟糕的链接文本 –>
要了解更多信息,请<a href="guide.html">点击这里</a>
要下载软件,请<a href="download.html">点击这里</a>

<!– 有意义的链接文本 –>
请阅读<a href="guide.html">无障碍开发完整指南</a>
请前往<a href="download.html">软件下载页面</a>

在第一个例子中,当用户浏览链接列表时,他们只能听到两个"点击这里",完全无法判断每个链接的目标是什么。第二个例子中的链接文本本身就包含了足够的信息,用户可以在不看周围内容的情况下做出选择。

表单的标签是另一个关键的无障碍要素。每个表单输入都应该有一个明确关联的 label 元素。这种关联通过 for 属性和 id 属性的匹配来实现:

<!– 没有标签关联的表单 –>
<div>
<span>姓名:</span>
<input type="text" name="name">
</div>

<!– 正确使用 label 的表单 –>
<div>
<label for="user-name">姓名:</label>
<input type="text" id="user-name" name="name">
</div>

在第一个例子中,屏幕阅读器可能无法正确识别 span 文本与输入框之间的关联。在第二个例子中,label 的 for 属性和 input 的 id 属性建立了明确的对应关系。即使用户点击 label 文字,焦点也会自动移到对应的输入框上,这对行动障碍用户也大有帮助。

数据表格的无障碍同样需要关注。一个简单的表格可能长这样,但缺少无障碍信息:

<table>
<tr>
<td>姓名</td><td>年龄</td><td>城市</td>
</tr>
<tr>
<td>张三</td><td>28</td><td>北京</td>
</tr>
</table>

屏幕阅读器用户无法区分哪些是表头、哪些是数据。正确的做法是使用 th 元素标记表头,并使用 scope 属性明确表头是应用于行还是列,同时用 caption 元素为表格提供标题:

<table>
<caption>员工信息表</caption>
<tr>
<th scope="col">姓名</th>
<th scope="col">年龄</th>
<th scope="col">城市</th>
</tr>
<tr>
<td>张三</td>
<td>28</td>
<td>北京</td>
</tr>
</table>

scope 属性值为 col 表示这个表头适用于整列,值为 row 表示适用于整行。有了这些信息,屏幕阅读器在朗读数据单元格时,会先读出对应的表头内容,比如"姓名:张三,年龄:28,城市:北京",让用户清楚地知道每个数据的含义。

CSS 的无障碍考量:颜色对比度与隐藏内容

CSS 虽然不像 HTML 那样直接提供无障碍特性,但使用不当同样会造成无障碍问题。首要的原则是:使用正确的 HTML 语义元素,用 CSS 来实现视觉效果,而不是反过来用 HTML 元素来强行实现某种外观。例如,如果你需要大号字体,应该使用 CSS 的 font-size 属性,而不是用 h1 标签来假装大号文字。

颜色对比度是一个需要特别注意的方面。文本的前景色和背景色之间必须有足够的对比度,才能被视力低下或色盲用户清晰辨识。WebAIM 提供了在线颜色对比度检查器,可以帮助你验证配色方案是否符合标准。一个常见的错误是使用浅灰色文字放在白色背景上,虽然这种设计看起来优雅,但对很多用户来说几乎无法阅读。

另一个重要的原则是:不要仅仅依赖颜色来传达信息。例如,在表单中将必填字段的边框设置为红色来提示用户,这对于色盲用户来说可能完全无法感知。应该同时使用颜色和其他标识,比如在红色边框旁边加上一个星号:

<!– 仅依赖颜色 –>
<label style="color: red;">邮箱:</label>
<input type="email">

<!– 同时使用颜色和符号 –>
<label style="color: red;">邮箱 *:</label>
<input type="email">
<span class="hint">(* 为必填项)</span>

关于内容的隐藏,有几种不同的 CSS 方式,它们对屏幕阅读器的影响各不相同。绝对定位将元素移出视觉区域但保留在 DOM 流中,这种方式对屏幕阅读器仍然是可见的。而使用 visibility: hidden 或 display: none 则会让屏幕阅读器也无法访问这些内容。除非你有明确的理由需要对屏幕阅读器也隐藏某些内容,否则应该优先使用前一种方式:

/* 对屏幕阅读器友好的隐藏方式 */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

<!– 使用示例 –>
<span class="sr-only">此信息仅对屏幕阅读器可见</span>

这个 CSS 类将内容在视觉上完全隐藏,但仍然保留在无障碍树中,屏幕阅读器可以正常读取。这对于为屏幕阅读器用户提供额外的导航信息非常有用。

JavaScript 与 WAI-ARIA:复杂场景的无障碍方案

JavaScript 在无障碍方面面临的核心挑战是:当页面内容动态更新时,屏幕阅读器用户可能完全不知道发生了变化。这在单页应用中尤其突出,因为内容通常在用户操作后通过异步请求更新,而这些更新对屏幕阅读器来说是静默的。

WAI-ARIA 规范提供了一套属性来解决这类问题。其中,aria-live 属性用于标记一个会动态更新内容的区域,并指示屏幕阅读器如何处理更新。它支持以下几个值:

  • off:默认值,不朗读更新内容。
  • polite:在用户当前操作完成后再朗读更新内容。
  • assertive:以较高优先级朗读更新内容。
  • rude:立即朗读,即使会打断用户当前的阅读。

一个典型的用法如下:

<p>
<span id="live-region" aria-live="polite" aria-atomic="false"></span>
</p>

当这个 span 的内容通过 JavaScript 更新时,屏幕阅读器会在用户空闲时自动朗读新内容。aria-atomic 属性如果设置为 true,则整个区域的内容都会被完整朗读;如果为 false,则只朗读发生变化的部分。

对于复杂的自定义表单控件,如日期选择器、选项卡面板等,ARIA 提供了丰富的 role 属性和状态属性。例如,一个自定义的选项卡组件可以这样标记:

<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel-1">第一页</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">第二页</button>
</div>
<div role="tabpanel" id="panel-1">
第一页的内容
</div>
<div role="tabpanel" id="panel-2" hidden>
第二页的内容
</div>

role 属性告诉屏幕阅读器这些元素的角色,aria-selected 表示选项卡当前是否被选中,aria-controls 建立了选项卡和面板之间的对应关系。这样,屏幕阅读器用户就能理解这个自定义组件的行为模式,就像操作原生组件一样。

审计工具与屏幕阅读器测试

自动化审计工具能够快速发现页面中的无障碍问题。Wave 是一个优秀的在线工具,你只需输入网页地址,它就会返回带有问题标注的页面视图。Tenon 是另一个选择,它会输出详细的错误报告,包括问题指标、受影响的 WCAG 标准以及修复建议。

aXe 是 Deque 公司开发的工具,提供了 Chrome 和 Firefox 浏览器的扩展程序。安装后,浏览器的开发者工具中会新增一个无障碍选项卡,可以直接对当前页面进行审计。aXe 也可以通过 npm 安装,并集成到 Grunt、Gulp 等任务运行器,以及 Selenium、Jasmine 等测试框架中,实现自动化的无障碍测试流程。

然而,自动化工具只能发现一部分问题,真正的用户体验测试仍然不可替代。屏幕阅读器测试是其中最重要的一环。不同操作系统有不同的屏幕阅读器选择:macOS 和 iOS 内置了 VoiceOver,Windows 平台有免费的 NVDA 和付费的 JAWS,Android 有 TalkBack,Linux 有 Orca,Chrome 浏览器有 ChromeVox 扩展。

以 macOS 上的 VoiceOver 为例,通过按 Cmd + F5 来启动和关闭。首次启动时会有一个教程,强烈建议花时间学习。VoiceOver 使用一个修饰键来配合各种快捷键,避免与其他系统命令冲突,这个修饰键默认是 Ctrl + Option。基本的导航包括使用修饰键配合方向键来移动 VoiceOver 光标,使用修饰键加空格键来激活当前选中的项目。修饰键加 U 键会打开一个转盘,其中列出了页面上的标题、链接、表单控件等要素,方便快速导航。

在 Windows 上,NVDA 是最常用的免费屏幕阅读器。安装后通过 Ctrl + Alt + N 启动。NVDA 的修饰键默认是 Insert 键,也可以在启动时的欢迎界面中改为 CapsLock 键。常用的导航命令包括按 H 键跳转到下一个标题,按 K 键跳转到下一个链接,按 F 键跳转到下一个表单输入等。NVDA 在标识位置和操作反馈方面比 VoiceOver 更加细腻,但在复杂页面中可能会让人感到有些迷失。

当你熟悉了屏幕阅读器的基本操作后,可以系统性地测试以下内容:检查标题层级是否正确,检查链接文本是否独立有意义,检查表单标签是否与输入框正确关联,检查表格的标题和数据是否被正确朗读。将良好语义化的页面与糟糕语义化的页面分别用屏幕阅读器测试一遍,对比体验的差异,你会对语义化 HTML 的重要性有直观的体会。

无障碍测试清单与总结

一个系统化的测试清单可以帮助你确保项目的无障碍质量。以下是一个基础的检查列表:

首先,确保 HTML 尽可能语义化正确。使用 W3C 的验证工具和审计工具来检查标记的质量。其次,关闭 CSS 后检查内容是否仍然有意义且顺序合理。这模拟了屏幕阅读器以线性方式解析内容的情况。

在交互层面,确保所有功能都可以通过键盘访问。使用 Tab、Enter、空格键等对每个交互元素进行测试,检查焦点顺序是否符合逻辑。确保所有非文本内容都有文字替代方案,包括图片的 alt 属性和视频的字幕。

在视觉设计层面,使用颜色对比度检查工具验证配色方案,确保文字与背景之间的对比度满足标准。不要仅仅依赖颜色来传递信息。对于隐藏的内容,确认使用的是对屏幕阅读器友好的隐藏方式,而不是 display:none 或 visibility:hidden。

在动态功能层面,检查功能在 JavaScript 不可用的情况下是否仍然基本可用,确保核心功能不依赖脚本。对于复杂交互组件,适当使用 ARIA 属性来增强无障碍信息。最后,在你的网站上发布一份无障碍声明,说明你为提升可访问性所做的努力,以及用户遇到问题时的联系渠道。

总之,无障碍测试不是项目结束前的最后一步,而是应该贯穿整个开发周期。从编写第一行 HTML 开始,到添加样式和脚本,再到最终的集成测试,每个阶段都应该考虑无障碍的影响。当你将这些实践内化为开发习惯时,你会发现构建可访问的网站并不会增加多少额外的工作量,而你的产品将能够触达更广泛的用户群体。

赞(0)
未经允许不得转载:171主机测评 » 跟着 MDN 学无障碍 Day 2:解决常见的无障碍问题——从 HTML 到实战测试
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址