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

role="button" 什么时候该用
它不是用来替代 <button></button> 的,而是当无法用原生按钮时的兜底方案。比如你用 <div> 或 <code><span></span> 实现了一个看起来像按钮的交互区域,又必须让屏幕阅读器识别为按钮、支持空格/回车触发,这时才加 role="button"。
常见误用场景包括:给已有 <button></button> 再加 role="button"(多余且可能干扰语义)、在纯展示元素上加(没配键盘事件就等于摆设)。
- 必须配合
tabindex="0",否则无法获得焦点 - 必须监听
keydown事件,手动处理Enter和Space键 - 建议同时设置
aria-pressed(用于切换类按钮)或aria-disabled(代替disabled属性)
为什么不能只写 role="button" 就完事
写了 role="button" 只是告诉辅助技术“这是个按钮”,但浏览器不会自动赋予任何行为——不聚焦、不响应空格/回车、不改变鼠标样式、不触发默认表单提交逻辑。它只是语义层的声明,不是功能层的实现。
典型错误现象:role="button" 元素用鼠标点正常,但键盘 tab 过去按空格没反应,或者屏幕阅读器读作“button”却无法操作。
- 缺失
tabindex="0"→ 无法键盘聚焦 - 没监听
keydown→ 空格/回车无响应 - 没改
cursor: pointer和:focus样式 → 视觉反馈缺失 - 没同步更新
aria-pressed→ 切换状态对辅助技术不可见
和原生 button 的关键差异在哪
原生 <button></button> 自带焦点管理、键盘交互(空格/回车触发)、表单提交行为、禁用逻辑、可访问性语义,而 role="button" 需要全部手动补全。性能上没区别,但维护成本高、易出兼容性问题(尤其旧版 IE 对 ARIA 支持弱)。
使用场景仅限于:组件库封装需要高度定制渲染结构(如用 <li> 做菜单项按钮)、遗留系统无法改标签、或框架限制导致无法直接用 <button></button>。
-
<button></button>自动有type="submit"行为,role="button"完全没有 -
<button disabled></button>会阻止所有交互并灰显,role="button"必须靠aria-disabled="true"+ 手动 CSS + 阻止事件 - 部分安卓 TalkBack 或 iOS VoiceOver 在某些 WebView 中对
role="button"的支持不如原生<button></button>
一个最小可用示例长什么样
以下代码能通过键盘和鼠标触发,被屏幕阅读器正确识别,并具备基本视觉反馈:
<div role="button" tabindex="0" aria-label="删除选中项">?️ 删除</div>
对应 JS 必须包含:
element.addEventListener('keydown', (e) => {
if (e.code === 'Enter' || e.code === 'Space') {
e.preventDefault();
handleClick();
}
});
element.addEventListener('click', handleClick);
别漏掉 e.preventDefault()——空格键默认会滚动页面,必须拦截。
真正麻烦的不是加 role="button",而是让它在所有主流辅助技术和键盘工作流里都表现一致。多数团队低估了这部分测试成本。











