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

别用 role="button" 包裹纯展示性 div —— 它不是“加语义”的补丁,而是给可交互控件兜底的逃生通道。
什么时候必须加 role="button"?
只有一种情况:你**不得不**用 div(或 span)来实现按钮行为,且无法改用原生 <button></button>。
常见真实场景包括:
- 组件库封装时,外层容器需响应点击(如卡片整体可点击),但内部已有
<button></button>用于子操作,再套一层<button></button>违反嵌套规则 - CSS Grid/Flex 布局中,父容器需保持 display: contents 或需避免块级约束,而
<button></button>会强制盒模型 - 旧项目强耦合了
div结构,短期无法重构 DOM,但需快速提升无障碍支持
注意:role="button" 不是“让 div 变成按钮”的魔法开关,它只告诉辅助技术“这个元素在功能上等价于按钮”。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
加了 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()">× —— 缺失键盘事件处理,空格键无效,且无 <code>aria-label。
哪些情况绝对不能加 role="button"?
以下写法属于语义污染,会破坏辅助技术判断:
-
<button role="button"></button>:原生<button></button>已隐式携带role="button",重复声明可能在旧版 NVDA 中被识别为“未知按钮” -
<div role="button">立即购买</div>且无任何交互逻辑:这是“假按钮”,既不可点也不可聚焦,只骗过了代码检查器 -
<a href="#" role="button"></a>:链接本应导航,覆盖为 button 后行为与语义冲突,键盘用户按 Enter 会触发跳转而非动作 <div role="button" aria-disabled="true">:禁用状态必须用原生 <code>disabled属性(仅对表单控件有效),或手动移除tabindex、监听并忽略所有事件,aria-disabled仅作视觉/语义提示,不阻断交互真正可靠的禁用方式:
<div role="button" tabindex="-1" aria-disabled="true"> + CSS 灰显 + JS 中完全跳过事件处理逻辑。 <h3> <code>role="button"的替代方案永远优先考虑绝大多数情况下,你应该直接用
<button type="button"></button>,而不是费力模拟:- 它自动获得
tabindex="0"、role="button"、Enter/Space 响应、disabled状态管理 - 支持
form属性脱离表单上下文提交指定表单 - 可通过
appearance: none+ 完全自定义样式,无需牺牲语义 - 服务端渲染(SSR)或静态 HTML 中,
<button></button>比带role的div更易被搜索引擎和爬虫理解
真正难搞的,从来不是“怎么加
role”,而是“为什么非得绕开原生语义”——那个原因,往往才是该重构的根因。- 它自动获得










