tabindex仅支持-1、0两种有效值:0使非原生元素按DOM顺序自然入Tab流,需同步加role、监听keydown并设:focus-visible;-1仅支持编程聚焦,正整数会破坏焦点流且应禁用。

tabindex 不是用来“给元素编号排队”的,真正能用的只有 0 和 -1;正整数(如 1、2)看似可控,实际会打乱焦点流、误导屏幕阅读器、且在动态渲染中极易失效。
什么时候该用 tabindex="0"
它只做一件事:让非原生可聚焦元素(比如 <div>、<span>)按 HTML 源码顺序自然加入 Tab 流。不是“让它第一个被聚焦”,而是“让它遵守 DOM 顺序”。
- 原生可聚焦元素(
<button>、<a href>、<input>)默认已有等效tabindex="0",加了冗余但无害 - 自定义控件(如
<div role="button">)必须加tabindex="0",否则键盘用户根本 Tab 不到 - 加完后不会自动响应
Enter或Space—— 必须手动监听keydown,判断event.key === 'Enter'或event.key === ' '(注意是空格字符) - 必须同步加
role属性(如role="button"),否则屏幕阅读器读不出语义 - CSS 若重置了 outline,得补上
:focus-visible或:focus样式,否则焦点不可见
为什么 tabindex="-1" 不是“隐藏”,而是“接管”
设为 -1 后,元素无法被 Tab 键到达,但能被 .focus() 主动聚焦——这是模态框、选项卡面板、折叠区域等动态组件的核心控制点。
- 模态框打开时,应立即对第一个可操作元素(如确认按钮)调用
.focus(),前提是它有tabindex="-1" - Accordion 展开后,焦点要移到新暴露内容里的首个可操作项,而不是留在触发按钮上
- 不能用
tabindex="-1"来“屏蔽”原生可聚焦元素(如<button>),那等于主动踢出 Tab 流;真要禁用,该用disabled或aria-disabled="true" - 移动端 Safari 在未触发用户手势前,
.focus()对非原生元素可能静默失败,需提前测试 - 元素若被
display: none或visibility: hidden隐藏,.focus()会失败,哪怕有tabindex="-1"
为什么绝对不要用 tabindex="1"、tabindex="2" 这类正整数
浏览器只看是否大于 0,不严格按数值排序;多个正数时虽按升序排列,但极易导致焦点流断裂、维护混乱和辅助技术误读。
立即学习“前端免费学习笔记(深入)”;
- 设了
tabindex="1"的<div>会跑到所有默认可聚焦元素(如<button>)前面,后者实际被挤到最后 - 常见错误是给多个按钮都设
tabindex="1",结果只有第一个生效——浏览器只认最小正整数,其余被忽略 - DOM 中插入新元素,整个正数序列需重算;SSR 渲染时客户端与服务端值不一致还会触发 hydration 警告
- 屏幕阅读器依赖 DOM 顺序理解上下文,强行插队会让线性导航逻辑崩溃
- React/Vue 动态渲染场景下,写死正数
tabindex几乎必然导致焦点路径错乱
真正难的不是选对某个 tabindex 值,而是判断哪些元素该进 Tab 流、哪些该由脚本主动控制、哪些该彻底隔离——可访问性不是加属性,是做决策;而最容易被忽略的一点是:焦点流本质是线性、不可跳变的路径,任何试图用正整数“插队”的做法,都会在动态内容、组件复用或 SSR 场景下露出破绽。



















