tabindex合法值仅0和-1,正整数必须删除;必须加tabindex="0"的是非原生但需键盘访问的容器(如<div role="button">),原生控件(<button>、<input>等)和纯装饰元素绝对不可加。

tabindex 的合法值只有 0 和 -1;所有正整数(如 tabindex="1")必须删除,否则键盘用户和屏幕阅读器会同步迷失。
哪些元素必须加 tabindex="0",哪些绝对不能加
只对默认不可聚焦、但业务上必须被键盘访问的容器加 tabindex="0",比如 <div role="button">、<h3 role="tab">、自定义折叠面板标题栏。这些元素加了之后,才按 DOM 顺序自然进入 Tab 流。
原生可聚焦元素(<button>、<a href>、<input>、<select>、<textarea>)**绝对不要加** tabindex="0"——冗余,还可能干扰 React/Vue hydration 或旧浏览器行为。
纯装饰性元素(如图标 <svg> 容器、仅用于视觉提示的 <span>)加了 tabindex="0" 会制造无效停靠点,应直接移除。
立即学习“前端免费学习笔记(深入)”;
tabindex="-1" 不是隐藏开关,是编程聚焦入口
它让元素无法被 Tab 键到达,但可通过 JS 调用 .focus() 主动聚焦。这是模态框、下拉菜单、编辑器临时 UI 中接管焦点的唯一可靠方式。
常见错误现象:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 模态框打开后调用
modal.querySelector('button').focus()失败,控制台无报错但焦点没动——大概率是那个按钮是<div>,没提前设tabindex="-1"; -
<button>元素加了tabindex="-1",结果被主动踢出 Tab 流,反而破坏默认行为; - 元素被
display: none或visibility: hidden隐藏,.focus()会静默失败,哪怕有tabindex="-1"。
注意:focus({preventScroll: true}) 在 Safari 中不支持,传对象参数会失效;生产环境建议先检测:if ('preventScroll' in FocusOptions.prototype)。
为什么 tabindex="1" 是最危险的写法
它不会让元素变成“第一个被 Tab 到的”,只会把它塞进所有正数里按升序排,且所有正数元素都会排在原生可聚焦元素之后——导致导航路径断裂。
更糟的是:
- 多个
tabindex="1"元素中,浏览器只认 DOM 中第一个,其余被忽略; - 混用
tabindex="1"和tabindex="2"后,焦点顺序变成“正数升序 → DOM 顺序 →tabindex="0"元素”,完全脱离用户预期; - React/Vue 动态渲染或 SSR hydration 场景下,这个值根本不可维护,组件重挂载后焦点错乱。
移动端 Safari 对正整数 tabindex 支持极差,甚至直接无视;iOS 上多数 <div tabindex="1"> 根本不会获得焦点。
加了 tabindex="0" 后仍无法用键盘触发交互?
加了只解决“能聚焦”,不等于“能响应”。焦点进入后,还需正确绑定事件和处理按键逻辑:
- 只监听
click事件不够:键盘用户按Enter或Space不会触发click,需额外监听keydown并判断event.key === 'Enter'或event.key === ' '(注意是空格字符); -
<button>上空格键自动触发click,但在<div tabindex="0">上不会——必须手写逻辑; - 必须同步设
role,例如role="button",否则屏幕阅读器读作“普通容器”,用户无法理解用途; - CSS 若清除了
outline,必须补上:focus-visible样式,否则焦点位置不可见。
表格行 <tr> 默认不可聚焦,要让它参与 Tab 流,只能加 tabindex="0",不能用 tabindex="-1"(否则用户按 Tab 会跳过整行),也绝不能用正整数。


















