必须加role="button"仅当不得不使用div/span实现按钮行为且无法改用原生button;此时还需同步添加tabindex="0"、onkeydown处理enter/space、aria-label或可见文本及视觉焦点样式,缺一不可。

非按钮元素加 role="button" 之前必须先解决语义缺失
纯 div 或 span 点击后表现像按钮,但对键盘用户和屏幕阅读器来说就是“不可聚焦、不可触发、无状态”的死元素。只加 role="button" 不够,它只是告诉辅助技术“这看起来像按钮”,但没赋予原生按钮的交互能力。
必须同步做三件事:
-
tabindex="0"让元素进入键盘焦点流(tabindex="-1"只能 JS 主动聚焦,不能靠 Tab 键到达) - 绑定
keydown事件监听Enter和Space键,手动触发点击逻辑(原生button自带该行为) - 设置
aria-disabled="true"并配合 CSS 视觉禁用,否则键盘用户仍能聚焦并触发——而原生button[disabled]会自动退出焦点流
role="button" 和原生 button 的关键行为差异
两者在 DOM 结构上可能一样,但底层行为完全不同。浏览器对原生 button 的处理是硬编码的:空格/回车触发、自动禁用时移出焦点流、默认可提交表单、自带 focus outline 样式。
用 div 模拟时,这些都得手动补全:
- 不设
type="button"的原生button在表单内默认会触发表单提交,这是常见错误;模拟元素则完全绕过表单机制,需自行判断是否要preventDefault() - 原生
button的:focus-visible样式开箱即用;模拟按钮需额外写 CSS 或依赖 JS 检测聚焦方式 - 屏幕阅读器读原生
button会说“按钮”,读div role="button"也会说“按钮”,但若没处理键盘事件,用户按空格后毫无反应,体验断裂
哪些场景真该用 role="button"?哪些纯属偷懒
真正需要它的场景极少,典型如:动态生成的图标按钮(SVG 内嵌在 div 中)、第三方组件库强制用 div 渲染但又要求按钮语义、或遗留系统无法改标签结构。
以下情况一律不该用,应直接换原生标签:
- 任何带文字内容的交互元素——直接用
button或a(链接语义正确时) - 表单提交控件——必须用
button[type="submit"]或input[type="submit"],否则无法被表单逻辑识别 - 开关类控件(如 toggle)——该用
input[type="checkbox"]+label,而非div role="button"+ 手动管理选中态
禁用状态下的焦点行为最容易被忽略
很多人给模拟按钮加 aria-disabled="true" 就以为完事了,但键盘用户 Tab 到它时依然能聚焦——这违反 WCAG 2.1 2.1.1(键盘),也违背用户预期。
正确做法是:
- 禁用时移除
tabindex="0"(或设为-1),确保它彻底退出键盘导航流 - 用 CSS 同步隐藏 focus outline,避免视觉焦点停在不可操作元素上
- 不要仅靠
pointer-events: none或opacity: 0.5表示禁用——这些对辅助技术无效,且不阻止键盘聚焦
真正难的是状态同步:JS 控制启用/禁用时,DOM 属性、CSS 类、键盘可访问性、屏幕阅读器播报,四者必须严格一致。差一个环节,可访问性就断链。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











