tabindex="0" 是将不可聚焦元素按 DOM 位置纳入默认 Tab 流的最小干预方式,不是“排第一”开关;tabindex="-1" 用于编程聚焦,非隐藏或禁用;正整数 tabindex 会破坏导航逻辑,WCAG 不推荐。

tabindex="0" 是什么,不是什么
它不是“让元素排第一”的快捷开关,而是把一个原本不可聚焦的元素(比如 <div>、<span>)按当前 DOM 位置“补进”默认 Tab 流的最小干预方式。原生可聚焦元素(<button>、<a href>、<input> 等)默认就等价于 tabindex="0",再显式加一遍纯属冗余,甚至在 React/Vue 动态渲染中引发 hydration 警告。
常见错误现象:给所有按钮统一加 tabindex="0",结果键盘用户 Tab 时反复聚焦同一个按钮两次(一次是原生行为,一次是冗余属性触发)。
- 只对非原生交互元素用
tabindex="0",例如<div role="button"> - 必须同步加
role属性(如role="button"),否则屏幕阅读器读作“div”,不是功能控件 - 必须监听
keydown,判断event.key === 'Enter'或event.key === ' '(注意空格是字符串空格),并调用对应逻辑 - 必须配
:focus-visible { outline: 2px solid #007bff; },清掉默认outline后不补这个,键盘用户就“看不见焦点在哪”
tabindex="-1" 的真实用途是编程聚焦,不是隐藏
设成 -1 后,元素彻底退出 Tab 流,但能被 .focus() 主动聚焦——这是模态框、选项卡、下拉菜单等动态组件的焦点控制核心。它不是“视觉隐藏开关”,也不是“禁用表单字段”的取巧手段(disabled 才是正确做法)。
常见错误现象:模态框打开后焦点没落到关闭按钮上,或点击 tab 按钮后焦点仍卡在按钮上,没跳进内容面板。
立即学习“前端免费学习笔记(深入)”;
- 模态框首个可操作元素(如确认按钮)需提前设
tabindex="-1",打开后立即element.focus() - 选项卡切换后,新暴露的
tabpanel内第一个<input>也得设tabindex="-1",再.focus() -
.focus()不自动滚动,必须紧跟element.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - 移动端 Safari 在未触发用户手势前,对非原生元素调
.focus()可能静默失败,需兜底检测
为什么绝对不要用 tabindex="1"、tabindex="2"
正整数 tabindex 不是“排序编号”,而是强制打乱浏览器默认 Tab 流的危险操作。多个 tabindex="1" 元素之间仍按 DOM 顺序聚焦;混用 tabindex="1" 和 tabindex="3" 时,tabindex="2" 缺失不会补位,且所有正数都会挤在原生可聚焦元素之前,导致导航路径断裂。
常见错误现象:表单里按钮设了 tabindex="2",输入框设了 tabindex="1",结果 Tab 键先到按钮,再跳到页面底部另一个 tabindex="1" 链接,中间的输入框被跳过。
- React/Vue 中组件重渲染后 DOM 插入顺序变化,硬编码的正数
tabindex会直接失效 - Safari 对嵌套 iframe 或动态插入的正数
tabindex支持不稳定,常静默忽略 - 屏幕阅读器依赖文档流逻辑,强行把页脚按钮设成
tabindex="1",会让用户认知断层 - WCAG 明确不推荐,它破坏的是整个键盘用户的导航预期,不是微调问题
真正可控的 Tab 顺序只有两种路径
一种靠 HTML 源码顺序——把需要先填的字段写在前面,哪怕视觉上它在右侧;另一种靠 tabindex="0" 补位,仅用于非原生元素。别指望 CSS order 或 JS 插入能绕过 DOM 顺序,Tab 键只认源码位置,不认渲染后布局。
常见错误现象:用 Flex/Grid 把右栏字段视觉上提前,但 HTML 仍是左→右写,结果键盘用户 Tab 时先填左栏,再跳到右栏顶部,中间的按钮被跳过。
- 视觉与 DOM 顺序不一致时,优先重构 HTML:把右栏字段提前写在左栏之前
- 必须用 CSS
order调整视觉时,确保原生可聚焦元素(<button>、<input>)仍在语义合理的位置,不依赖tabindex强行修正 - 动态插入字段后,不要依赖
tabindex排序,而应插入后立即element.focus()并scrollIntoView() - 移动端 Safari 默认只允许
<input>、<textarea>、<select>、<button>和contenteditable元素参与 Tab 导航,tabindex="0"对<div>无效



















