必须用 role="button" 的唯一情况是:无法使用原生而必须用div或span实现按钮行为,且需补无障碍支持;此时还需添加tabindex="0"、处理enter/space键、提供aria-label或可见文本、实现视觉焦点反馈。

什么时候必须用 role="button"?
只有一种情况:你**不得不**用 div(或 span)实现按钮行为,且无法改用原生 <button></button>。比如:
- 组件库中卡片整体可点击,但内部已有子
<button></button>,再套一层会违反嵌套规则 - CSS Grid/Flex 布局要求父容器保持
display: contents,而<button></button>会强制生成盒模型 - 遗留系统 DOM 结构强耦合
div,短期无法重构,但需快速补无障碍支持
别把它当成“让 div 变成按钮”的快捷方式——role="button" 不提供任何原生能力,只是向辅助技术声明“这个元素功能上等价于按钮”。
加了 role="button" 就完事了吗?
不。缺以下任一环节,键盘用户和屏幕阅读器都会失败:
-
tabindex="0":否则无法通过 Tab 键进入焦点流(tabindex="-1"只允许 JS 主动聚焦) -
onclick+onkeydown:必须监听Enter和Space键,并调用event.preventDefault(),否则空格键会滚动页面 -
aria-label或可见文本:若内容为空(如仅含 SVG),必须提供明确功能描述,例如aria-label="关闭弹窗" - 视觉反馈:需用
:focus、:active等伪类提供焦点与按下状态样式,否则键盘用户无法确认当前操作目标
错误示例:<div role="button" tabindex="0" onclick="doAction()">×</div> —— 缺失键盘事件处理,无 aria-label,空格键无效。
哪些写法绝对不能做?
这些是语义污染,会破坏辅助技术判断:
-
<button role="button"></button>:原生<button></button>已隐式携带role="button",重复声明可能在旧版 NVDA 中被识别为“未知按钮” -
<div role="button">立即购买</div>且无任何交互逻辑:这是“假按钮”,既不可点也不可聚焦,只骗过了代码检查器 <div role="button" aria-disabled="true">:禁用状态必须用原生 <code>disabled属性(仅对表单控件有效),或手动移除tabindex、监听并忽略所有事件;aria-disabled仅作视觉/语义提示,不阻断交互-
@#@#@#@#@#@#@#@#@#@0:链接本应导航,覆盖为 button 后行为与语义冲突,键盘用户按 Enter 会跳转而非执行动作 - 不要运行时改
role,改用aria-live+ 动态文本更新来传达状态变化 - 如果真需切换角色(极少见),应销毁旧元素、重建新元素并重新聚焦
- 检查是否在
display: none或visibility: hidden元素上设置了role:这些元素本就不在可访问树中,设了也无效
动态更新 role 为什么没反应?
大多数辅助技术只在元素插入 DOM 时读取一次 role。运行时修改(比如从 role="button" 改成 role="alert")不会触发语义重计算。
正确做法是:
真正难的不是加 role,而是确保语义、行为、样式、焦点管理四者完全对齐——漏掉任意一环,对残障用户的体验就是断裂的。











