focus order 是浏览器默认的焦点路径,由 DOM 顺序与可聚焦性共同决定,非单纯依赖 tabindex 数值;它受元素类型、tabindex 值、显示状态、aria-hidden 等动态影响,必须匹配视觉逻辑流以满足无障碍要求。

focus order 是什么,不是 tabindex 顺序
浏览器默认的焦点路径(focus order)由 DOM 顺序 + 可聚焦性共同决定,不是你写了 tabindex="1" 就一定排第一。它本质是“可聚焦元素在文档流中的自然遍历路径”,受 tabindex 值、元素类型、显示状态、aria-hidden 等多因素动态影响。
常见错误是给一堆 div 手动设 tabindex="1"–"5",结果焦点跳转混乱、绕回 body、甚至卡死。因为:原生可聚焦元素(如 button、a[href]、input)天然拥有 tabindex="0",它们会插队到你自定义的正整数序列中间。
-
tabindex="-1"的元素不会进入 Tab 序列,但可通过 JS 主动聚焦(比如模态框打开后聚焦第一个输入框) -
tabindex="0"让非原生元素加入默认流,顺序严格按 DOM 位置,不与正整数冲突 - 隐藏元素(
display: none或aria-hidden="true")自动从焦点路径中剔除,哪怕它有tabindex="0"
DOM 顺序必须匹配视觉逻辑流
屏幕阅读器用户和键盘用户依赖同一套焦点路径。如果页面视觉上是“标题 → 表单 → 提交按钮”,但 HTML 结构写成“表单 → 标题 → 提交按钮”,焦点就会先跳进输入框,再跳回标题——这破坏了认知连贯性,也违反 WCAG 2.1 的 Focus Order 成功标准(2.4.3)。
重构时别靠 CSS flex-direction: column-reverse 或 position: absolute 打乱视觉流;而是用语义化结构+合理嵌套保证 DOM 顺序即用户预期路径:
立即学习“前端免费学习笔记(深入)”;
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 用
main包裹核心内容,aside放辅助信息,避免把侧边栏写在main前面 - 导航菜单用
nav+ul,不要用div堆砌链接,否则焦点顺序可能和视觉顺序错位 - 表单字段按操作流程排列:
label+input+error message必须紧邻,不能把错误提示塞到页面底部再用 JS 移动
动态内容插入时 focus order 会断裂
JS 动态插入新元素(如加载更多列表项、异步表单校验提示)若直接 appendChild 到 DOM 末尾,新元素会出现在焦点路径最后——即使它视觉上就在当前输入框下方。用户按 Tab 键会跳过它,直接到页脚或下一个可聚焦元素。
修复的关键不是强行 element.focus(),而是让新节点插入到逻辑位置:
- 表单校验失败提示,应插入到对应
input的紧邻后(用insertAdjacentElement('afterend', errorEl)) - “加载更多”按钮新增的卡片,应追加到现有卡片列表的
lastChild后,而非document.body末尾 - 模态框内容必须插入在触发按钮之后(或用
aria-modal="true"配合焦点陷阱),否则焦点路径会穿透到背景层
role="application" 会彻底关闭默认 focus order
极少数场景(如代码编辑器、绘图画布)需完全接管键盘行为,会用 role="application"。但它会禁用浏览器默认焦点管理——所有焦点移动、Tab 键行为都得手写实现,且必须同时支持屏幕阅读器的虚拟光标模式。
绝大多数 Web 应用不该用它。误用后果严重:
- Tab 键失效,用户无法切换元素
- 屏幕阅读器退出“浏览模式”,进入“应用模式”,失去 heading / list / link 等语义导航能力
- 焦点状态无法被辅助技术感知,
aria-activedescendant等机制变得极其脆弱
真正需要精细控制的组件(如树形控件、日历),应优先用 role="tree" / role="grid" 等语义角色,配合 tabindex="0" 和键盘事件,而不是一刀切关掉整个焦点系统。


















