焦点顺序由tabindex数值决定而非DOM顺序:负数最前,0保持自然流,正数按值升序排列;应避免tabindex>0,优先用DOM顺序和tabindex="0",必要时用tabindex="-1"+脚本控制。

focus 顺序由 tabindex 决定,不是 DOM 顺序
浏览器默认按 HTML 中元素出现的顺序切换焦点,但这只在所有 tabindex 值为 0 或未设置时成立。一旦设置了 tabindex,焦点顺序就完全由它的数值控制——不是“先写谁就先聚焦”,而是按数值从小到大排列,负数排最前,然后是 0,再是正数。
常见错误是以为把 tabindex="1" 加到第一个输入框就能“从头开始”,结果发现其他带 tabindex="2" 的按钮或链接插队了——因为所有显式设了正数 tabindex 的元素都会被提前排序,挤掉原本的自然流。
-
tabindex="-1":元素可被脚本聚焦(el.focus()),但无法通过 Tab 键到达 -
tabindex="0":保留在自然顺序中,和没设一样,只是显式声明“可聚焦” -
tabindex="1"、tabindex="2"等:强制插入焦点序列,值越小越靠前;多个相同值时,按 DOM 顺序决定先后
避免 tabindex > 0,除非有明确交互逻辑
给表单控件设 tabindex="1"、tabindex="2" 看似可控,实则埋雷:一旦后续加了新字段(比如弹窗里的搜索框也设了 tabindex="3"),整个顺序就乱了;更糟的是,屏幕阅读器会严格按这个顺序播报,可能跳过语义上更该优先的主操作区。
真正需要干预顺序的场景其实很少,比如模态框内必须限制 Tab 只在内部循环,或表单顶部有个“跳转到内容”链接需优先获得焦点——这时用 tabindex="-1" + 脚本控制更稳妥。
立即学习“前端免费学习笔记(深入)”;
- 表单字段尽量不设正数
tabindex,依赖 DOM 顺序和tabindex="0"即可 - 若必须重排,用最小必要集合:只给关键跳转点(如“跳至搜索”)设
tabindex="1",其余保持tabindex="0"或不设 - 移动端 Safari 对正数
tabindex支持不稳定,部分版本直接忽略
用 JavaScript 动态控制 focus 更可靠
硬编码 tabindex 容易失控,而用脚本在特定时机调用 focus() 或 setFocus(),既能响应用户操作,又不破坏语义顺序。
例如提交失败后让第一个出错字段获得焦点:document.querySelector('.error input').focus();或者模态框打开后自动聚焦首输入框:modal.querySelector('input, select, textarea').focus()。
- 注意:调用
focus()前确保元素已渲染且display: block(隐藏元素调用无效) - 配合
autofocus属性可用于初始聚焦,但它会被 JS 覆盖,且不适用于动态插入的元素 - 若需循环聚焦(如对话框内 Tab 键不跳出),监听
keydown捕获 Tab 事件,手动preventDefault()并转移焦点
检查焦点顺序是否符合预期
别只靠眼睛看,用键盘真实测试:Shift+Tab 和 Tab 键反复切换,观察焦点停在哪、是否跳过不该跳的元素、是否卡死在某处。Chrome DevTools 的“Accessibility”面板能可视化 tab order,但最终以键盘行为为准。
容易被忽略的是:disabled 元素永远不参与焦点顺序,哪怕它有 tabindex="0";而 contenteditable 元素默认可聚焦,但若没设 tabindex,部分旧浏览器可能跳过。
- 用
document.activeElement实时查看当前焦点元素 - 运行
Array.from(document.querySelectorAll('[tabindex]')).sort((a, b) => a.tabIndex - b.tabIndex)查看当前所有显式 tabindex 的排序 - 无障碍测试工具(如 axe)会报出 tabindex > 0 的警告,不是 bug,但提示你确认是否真有必要



















