应优先使用原生 button 元素,因其天然支持键盘焦点、Enter/Space 触发、:active 状态、最小触控热区(44×44px),且在 Safari 和部分安卓 WebView 中不丢点击通道;改用 div 模拟需手动补全语义(role="button")、交互(click 事件)和可访问性(tabindex、focus 管理),否则会导致键盘用户操作断裂、屏幕阅读器无法识别、移动端响应延迟等问题。

为什么用 button 而不是 div + JS 模拟
原生 button 元素天然支持键盘焦点、Enter/Space 触发、:active 状态样式、最小触控热区(44×44px),且在 Safari 和部分安卓 WebView 中不会丢掉点击通道。换成 div class="btn" 后,浏览器不认为它是可交互元素:tabindex="0" 只解决聚焦,不解决语义、键盘激活或辅助技术识别。
常见错误包括:
- 给
div加role="button"却没同步处理aria-pressed或aria-expanded - 用
onclick绑定但忽略空格键触发,导致键盘用户必须按两次(先聚焦再按空格) - 重置样式时用了
all: unset却漏掉cursor: pointer和user-select: none,影响视觉反馈
keydown 监听按钮行为的坑在哪
直接监听 button 的 keydown 事件来响应 Enter 或 Space 是多余且危险的——click 事件本身已自动兼容这两者。强行监听反而容易干扰输入法(比如中文拼音上屏前的 key 值为 "Process")、漏掉 Shift+Enter 等组合,或在 contenteditable 区域内误触发。
真正需要监听 keydown 的场景只有两类:
立即学习“前端免费学习笔记(深入)”;
- 非标准交互:比如表格单元格用方向键导航,此时需
tabindex="0"+keydown处理ArrowUp/ArrowDown - 全局快捷键:如
Ctrl+S保存,必须绑定在document上,且要排除input、textarea、[contenteditable]
别在 button 上写 onkeydown="if(event.key==='Enter')doSomething()"——这等于自己造轮子还造歪了。
如何让自定义按钮真正支持键盘导航
如果因样式或布局限制必须用非原生标签(比如用 span 实现图标按钮),至少保证三点:
- 加
tabindex="0"让它能被 Tab 进入 - 加
role="button"告诉屏幕阅读器这是按钮 - 用
click事件(不是keydown)响应交互,并手动调用element.click()在 JS 中触发逻辑
动态插入的按钮(如列表渲染后追加)容易漏掉焦点管理:新按钮插入后,若用户正用键盘操作,焦点不会自动跳过去。解决方案是插入后立即调用 button.focus(),但仅限于用户明确触发的操作(比如点击“添加”后聚焦新项),避免打断当前浏览流。
移动端键盘响应慢?先查 DOM 结构
真机测试显示,6 层嵌套的 div 包裹一个按钮,比 3 层平均多 30ms 延迟——而人对“点击即响应”的容忍阈值是 100ms。延迟来源不是 JS 执行慢,而是事件冒泡路径过长、浏览器无法快速定位目标节点。
优化建议很具体:
- 按钮层级深度 ≤ 6 层(从
body算起) - 避免在按钮外层套
div再加onclick,直接把事件挂到button上 - 用
label包裹input type="checkbox",天然放大触控热区,无需额外 JS - 禁用
touch-action: manipulation的地方(比如轮播图容器)不要覆盖按钮区域,否则会阻断click事件
最常被忽略的一点:键盘焦点进入后,:focus-visible 样式没配,用户根本不知道当前聚焦在哪——这不是 bug,是体验断裂。



















