必须用<a>实现跳转,用<button>触发客户端逻辑;混用会破坏键盘导航、屏幕阅读器识别和原生交互行为,语义错误不可仅靠样式修复。

按钮该用 <button>,跳转该用 <a>——混用会直接破坏键盘导航、屏幕阅读器播报和默认行为,不是样式问题,是语义断裂。
什么时候必须用 <a> 而不是 <button>
目标是页面跳转(含锚点、外部 URL、mailto:、tel:)时,<a> 是唯一合规选择。浏览器原生支持它的键盘行为:Tab 可聚焦、Enter/Space 触发跳转、右键可复制链接、中键新开页——这些能力 <button> 无法模拟。
- 错误写法:
<button onclick="location.href='/about'">关于</button>→ 键盘用户按 Enter 会触发,但按 Space 不会;无法中键打开;屏幕阅读器读作“按钮”,而非“关于页面链接” - 正确写法:
<a href="/about">关于</a>→ 所有原生行为 intact,再用 CSS 样式化成按钮外观即可 - 若需新窗口跳转,加
target="_blank"和rel="noopener",不要靠 JS 模拟
什么时候必须用 <button> 而不是 <a>
触发客户端逻辑(如展开菜单、提交表单、播放视频、AJAX 加载)且不改变当前 URL 时,<button> 是唯一语义正确的标签。它自带 role="button"、可聚焦、支持 Enter/Space 双键触发、禁用态 disabled 属性开箱即用。
- 错误写法:
<a href="#" onclick="toggleMenu()">菜单</a>→ 默认行为是滚动到页首,需event.preventDefault()抑制;键盘用户按 Enter 后焦点丢失;无障碍树里它是“链接”,不是“按钮” - 正确写法:
<button type="button" onclick="toggleMenu()">菜单</button>→ 无副作用,语义清晰,禁用时自动失去焦点能力 - 表单内按钮优先用
type="submit"/type="reset",别全写type="button"再手动 submit
<a> 样式化为按钮时的可访问性红线
视觉上像按钮没问题,但不能牺牲语义和交互契约。关键不是“看起来像”,而是“行为像链接”且“被辅助技术识别为链接”。
立即学习“前端免费学习笔记(深入)”;
- 必须保留
href属性,禁止用href="#"或href="javascript:void(0)"—— 这会让屏幕阅读器读作“未指定地址的链接”,或直接跳过 - 禁用态不能只靠 CSS 灰显,得加
aria-disabled="true"并移除href(或设为href=""),否则键盘用户仍可聚焦并误触 - 若链接内容仅为图标(如
<a href="/cart"><i class="icon-cart"></i></a>),必须加aria-label="购物车",否则屏幕阅读器读不出意图 - 不要给
<a>加role="button"—— 它会覆盖原生语义,导致跳转行为被屏蔽,且违反 ARIA 实践准则
嵌套或并列交互元素时的常见崩坏点
当一个区域既要跳转又要执行 JS 行为(比如点击整行跳转,但行内有个删除图标),不能靠包裹或事件冒泡硬凑,必须拆解语义。
- 错误结构:
<div onclick="goToDetail(id)"><span>标题</span><button onclick="deleteItem(id)">×</button></div>→ 删除按钮焦点被外层div抢走;点击 × 会先触发外层跳转 - 正确结构:用
<a>包标题文字,<button>单独放删除操作,两者平级;外层用<article>或<li>容纳,不绑定交互 - 若真需整行响应(如移动端),用
<a>全宽包裹,删除按钮用aria-hidden="true"+tabindex="-1"移出焦点流,再靠 JS 绑定图标点击事件 —— 但前提是删除操作有独立可发现入口(如长按菜单)
最易被忽略的是:可访问性失效往往不在单个标签,而在父子关系与事件委托的耦合。一旦出现“点了没反应”“按空格没触发”“屏幕阅读器读错类型”,先查标签语义是否匹配行为,再查是否意外覆盖了原生交互链路。



















