语义化 HTML
根据元素的含义而非默认外观来使用 HTML 元素。`<button>` 会被声明为按钮;`<div onclick>` 则什么也不会声明。绝大多数无障碍问题都源于语义的缺失。
语义化 HTML 是指根据元素的含义而非其外观来选择 HTML 元素。<button> 用于操作,<nav> 包裹导航,<table> 承载表格数据。将一个 <div> 样式化为按钮来执行某个操作,是语义失败——即使视觉效果完全相同。
绝大多数无障碍缺陷都源于语义的缺失。几乎每一种辅助技术故障都有相同的根因:用通用元素代替了专为此设计的语义元素。
屏幕阅读器为何依赖语义
屏幕阅读器通过无障碍树将 HTML 内容呈现给用户——这是从 DOM 构建的内部表示,将每个元素映射到角色、名称、状态和属性。无障碍树才是用户实际”听到”的内容。
原生 <button> 在无障碍树中表示为 button "提交"。而样式化为按钮的 <div> 则表示为 generic——屏幕阅读器不会声明任何有用信息,用户也无从发现该 div 是可交互的。
同样的逻辑适用于:
- 标题(
<h1>–<h6>)——屏幕阅读器允许用户通过标题跳转来浏览页面。<div class="heading">不会出现在标题列表中。 - 列表(
<ul>、<ol>、<li>)——屏幕阅读器在朗读前会声明”列表,共 5 项”。伪装成列表项的<div>则不会。 - 表单——带有关联
<label>的<input>会被读作”电子邮件,编辑文本”。由 div 和 contenteditable 构成的自定义输入框则什么也不会声明。 - 表格——带有
<th>行列标题的<table>允许用户逐单元格导航,并同步声明标题上下文。由 div 组成的 CSS 网格则不具备此能力。
替换模式
修复方式几乎总是相同的:
<div onclick="...">→<button>(或用于导航的<a href>)。<span class="link">→<a href>。<div role="heading">→<h1>至<h6>。- 自定义下拉框 → 在设计允许时使用
<select>,否则使用经过模式验证的 ARIA combobox。 - 以样式化
<div>构建的标签页 → 参考 ARIA 创作实践指南中的标签页模式。
何时使用 ARIA
ARIA 的第一条规则是:若原生元素能胜任,请勿使用 ARIA。ARIA 角色、状态和属性是补丁,适用于原生 HTML 无法描述所构建组件的场景(树形视图、多选组合框、某些模态框模式)。它们不能替代使用 <button>。
实用审计方法
检测代码库中语义退化的最快方法:搜索 div 和 span 上的 onclick。每一处匹配都是潜在的失败点。次快的方法:搜索 role= 属性。ARIA 在有限场景下是合理的,但大量使用 ARIA 通常意味着原生元素被跳过,而跳过的原因往往是样式需求——但实际上并不需要这样做。