键盘导航与焦点管理是同一机制的两面,问题源于开发者未对齐行为预期:tab跳失、焦点被覆盖、safari首次加载失焦、快捷键无焦点守卫、showmodal与虚拟键盘滚动不同步,以及可聚焦元素未回答三个关键问题。

键盘导航和焦点管理不是冲突关系,而是同一套机制的两个侧面:键盘操作(比如 Tab、Shift+Tab、Enter)依赖焦点状态驱动,而焦点位置又决定了键盘行为是否生效。真正出问题的地方,是开发者没对齐这两者的行为预期。
为什么按 Tab 时焦点“跳丢了”
常见现象是:元素加了 tabindex="0",但按 Tab 就是不聚焦;或者焦点进了某个容器后,再按 Tab 就直接跳出页面,中间的可聚焦子项被跳过。
- 父容器用了
overflow: hidden且没设tabindex,内部tabindex="0"元素虽可聚焦,但 Tab 流程无法“感知”到它所在滚动上下文——浏览器不会自动滚动容器去显示它 - 某些框架或组件库在挂载/更新时调用
element.focus(),但没等 DOM 稳定就执行,导致焦点被后续渲染覆盖 - Safari 下,页面首次加载未触发“输入上下文”(如鼠标点击或键盘交互),原生可聚焦元素(
button、input)可能拒绝接受Tab进入,需用户先点一下页面再恢复
keydown 处理快捷键时,为什么输入框里按 Ctrl+F 也触发了
根本原因在于没做焦点守卫:快捷键逻辑全局监听,却没判断当前是否处于可编辑上下文。
- 必须检查
document.activeElement的tagName是否为'INPUT'、'TEXTAREA'、'SELECT' - 还要补充
!event.target.isContentEditable,否则contenteditable="true"的div也会漏判 - 优先用
keydown而非keyup,后者响应滞后,且无法拦截默认行为(如F5刷新、Ctrl+T开新页) - 调用
event.preventDefault()不是可选——只要你不拦,浏览器就按自己的规则处理
showModal() 自动聚焦 vs 虚拟键盘安全区域打架
移动端最典型的冲突场景:showModal() 把焦点锁进对话框第一个 input,但虚拟键盘弹出后,该 input 被遮挡,页面又没自动滚动到位,用户看不到自己在输什么。
-
showModal()的焦点锁定是强制的,但滚动行为由 UA 控制,两者不同步 - 不能靠
scrollIntoView()硬滚——它可能在键盘弹出前就执行,白忙活 - 稳妥做法是监听
focusin事件,在焦点真正落到input上后再延时requestAnimationFrame或setTimeout(..., 0)触发滚动 - 更底层的规避方式:给
input加autofocus属性,让浏览器在模态框打开时自主决策滚动时机,兼容性反而更好
最容易被忽略的不是怎么写代码,而是每处可聚焦元素是否都明确回答了三个问题:它该不该进 Tab 流?它获得焦点时页面是否可见?它失去焦点后,下一个焦点目标是否合理?这三个问题漏掉任何一个,键盘导航就会在某台设备、某个时刻突然断掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











