tabindex="0"是让非原生元素按dom顺序进入tab流的唯一合理方式,需同步添加role、监听keydown处理enter/空格键并提供:focus-visible样式;tabindex="-1"仅支持程序化聚焦,正整数tabindex必须禁用。

tabindex="0" 是让非原生元素进 Tab 流的唯一方式
它不改变 DOM 顺序,只声明“这个元素愿意按 HTML 结构自然加入键盘导航流”。比如一个 div 带 onclick,不加 tabindex="0",键盘用户就完全 Tab 不到它。
但加了只是“能被 Tab 到”,不是“能用”:
- 必须同步加
role,例如role="button",否则屏幕阅读器读不出可操作语义 - 必须监听
keydown,手动处理Enter和空格键(' '),并调用event.preventDefault()防止空格滚动页面 - 必须提供
:focus-visible样式,否则键盘用户看不到焦点在哪 - 别给纯展示性元素(如图标容器、装饰
span)加,会增加无效停靠点
tabindex="-1" 是程序化聚焦的唯一入口
它让元素彻底退出 Tab 键遍历路径,但保留被 .focus() 主动聚焦的能力。这是模态框、下拉菜单、折叠面板等动态组件控制焦点流转的核心机制。
常见误用是只写 .focus() 却没提前设 tabindex="-1",结果静默失败:
- 模态框内第一个按钮或输入框,HTML 中就得带
tabindex="-1",打开后立刻element.focus() - Accordion 展开后,焦点要落到新暴露的首项,而不是留在触发标题上
- 关闭弹层前,需记录触发源(如
document.activeElement),关完立即回焦 -
display: none或visibility: hidden的元素即使有tabindex="-1",.focus()也会失败
正整数 tabindex(如 "1", "2")为什么必须禁用
浏览器对正整数的处理逻辑是:所有 tabindex > 0 的元素会被提到整个 Tab 流最前面,再按数值升序排列;同值时仍退回到 DOM 顺序。这不是“精细排序”,而是破坏性重排。
实际后果很直接:
- 多个
tabindex="1"元素,只有第一个真正生效,其余被降级为0 - 原生可聚焦元素(
button、input)默认无tabindex,会被挤到最后,导致 Tab 从页脚按钮开始 - React/Vue 动态插入节点后,正数
tabindex容易错位,SSR 还可能触发 hydration 警告 - 第三方组件库若也用正数,Tab 流立刻失控,两个
tabindex="1"都变成“第一个”
容易被忽略的关键细节
tabindex="-1" 不等于视觉隐藏,也不阻止点击或提交——它只隔离 Tab 入口。要禁用交互,该用 disabled 或 inert,而不是靠 tabindex 假装控制。
移动端 Safari 在未触发用户手势前,对非原生元素调用 .focus() 会静默失败;focus({ preventScroll: true }) 在 Safari 上不支持,传对象参数会失效,生产环境建议先检测支持性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











