用 div 做的按钮丢掉的七样东西
一句话总结
如果不使用 <button> 而改用 <div onclick>,就会失去浏览器原本代为完成的七项工作,而人们通常不会重新实现其中五项。
问题为何出现——看不见的用户
如果用鼠标开发,再用鼠标验证,这类事故始终不会暴露,因为 <div onclick> 在鼠标操作下表现得非常完美。QA 也用鼠标检查,所以它会直接上线。
真正受影响的是只使用键盘的用户。他们可能手腕受伤,可能使用屏幕阅读器,也可能只是觉得 Tab 更快。对这些用户而言,那个按钮虽然显示在屏幕上,却只是一张无法点击的图片。如果它是支付按钮,他们就无法完成支付。
修复成本也会随着时间增加。工作不再只是更换一个标签,还要沿着它上方累积的 CSS 选择器、事件委托和测试选择器逐一修改。因此,本文讨论的不是“以后再处理的清单”,而是选择标签时就应作出的判断。
会失去什么
<div onclick="save()">저장</div> 会失去以下能力。
- 无法通过 Tab 到达——需要
tabindex="0"。 - 无法通过 Enter 或 Space 激活——必须自行绑定键盘处理器。
- 屏幕阅读器不会读作“按钮”——它只是普通文本。
- 无法禁用——
disabled不起作用。 - 无法提交表单——没有
type="submit"。 - 没有焦点环——键盘用户不知道当前位置。
- 没有浏览器默认行为——包括上下文菜单和自动化工具识别。
只要写下 <button>,这些能力就全部免费获得。可访问性不是之后附加的功能,而是在选择标签时就已经大体决定。
地标——页面地图
屏幕阅读器用户不会从页面顶部开始依次阅读,而会在各个地标之间跳转。
<header> 사이트 머리 </header>
<nav> 탐색 </nav>
<main> 본문 — 페이지에 하나만 </main>
<aside> 곁다리 </aside>
<footer> 꼬리 </footer>
存在 <main> 时,“跳到正文”功能才能工作。<div class="main"> 没有任何语义——类名不会向机器传达任何信息。
标题就是目录
h1~h6 不是字号,而是文档目录。屏幕阅读器会只浏览标题来理解结构。
h1只能有一个,用于说明页面主题。- 不要跳级——
h2后面不能直接出现h4。 - 如果想改变大小,应通过 CSS 修改,而不是更换标签。
因为 h3 看起来漂亮就使用它,是最常见的可访问性问题之一。
alt 不是“说明文字”
alt 是图片消失时用来填补其位置的文本。
| 图片 | alt |
|---|---|
| 产品照片 | alt="빨간 등산 배낭 45L" |
| 链接内的徽标 | alt="홈으로"——描述的不是图片,而是链接目的 |
| 装饰性分隔线 | alt=""——应当留空 |
| 旁边已有相同说明的图片 | alt=""——重复朗读会造成干扰 |
省略 alt 属性本身与写成 alt="" 并不相同。省略后,屏幕阅读器会朗读文件名(IMG_20240103.jpg);留空则会安静跳过。装饰图片必须明确写出 alt=""。
没有标签的输入框就没有名称
<!-- ❌ placeholder 는 라벨이 아니다 -->
<input placeholder="이메일">
<!-- ✅ -->
<label for="email">이메일</label>
<input id="email" type="email" autocomplete="email">
placeholder 会在输入后消失,用户将无法确认这个输入框原本要求填写什么。对于低视力用户,它的对比度也不足。
关联标签后,点击文字也会让输入框获得焦点,这在移动设备上尤其重要。
另外,正确设置 type 与 autocomplete 后,移动键盘会随之改变,自动填充也能工作,例如 type="email"、type="tel"、autocomplete="one-time-code"。
链接与按钮不同
链接用于跳转,按钮用于执行操作。
- 地址会变化 → 使用
<a href>。新标签页打开、书签和后退功能都能正常工作。 - 状态会变化 → 使用
<button>。
<a href="#" onclick="..."> 两者都不是。此外,链接文本本身必须说明目的。屏幕阅读器用户会抽取所有链接并以列表查看,如果列表中只有二十个“这里”和“查看更多”,就毫无用处。
表格必须有表头单元格
<table>
<caption>월별 매출</caption>
<thead><tr><th scope="col">월</th><th scope="col">매출</th></tr></thead>
<tbody><tr><th scope="row">1월</th><td>1,200</td></tr></tbody>
</table>
只有设置 scope,屏幕阅读器才会读成“1 月、销售额、1200”。否则只会连续朗读数字。
不要用表格实现布局。 那是 CSS Grid 的职责。
不要遗漏 lang
<html lang="ko">
缺少它时,屏幕阅读器会按照英语发音规则朗读韩语。这是仅修改一个字符就能取得巨大效果的改进之一。
ARIA 是最后手段
错误的 ARIA 还不如没有 ARIA。
即使添加 role="button",也不会自动获得键盘操作。它只改变了表面名称,实际行为完全不变。因此规则只有一条。
如果存在合适的 HTML 元素,就使用它。 只有 HTML 没有对应元素时,例如标签页、树或实时区域,才使用 ARIA。
如何在实际工作中检查
自动检查工具只能发现30~40% 的问题,因为它只能检查对比度不足、缺少 alt 等机器能够观察的问题。其余部分必须由人检查。
- 只用键盘完成整个流程。 使用 Tab、Shift+Tab、Enter、Space、Esc。把鼠标移开,5 分钟内就能暴露大多数问题。
- 当前焦点是否始终清晰可见?
- 放大到 200% 后是否仍可使用?
这三项比自动工具能发现更多问题。
实际工作中,最好把它固化为 PR 检查清单中的一行,这样更容易长期坚持。只需问一句:“新建的交互元素是否只用键盘操作过?”期待人们每次都靠记忆遵守的规则,最终一定会被遗漏。