tabindex="0"是让非原生元素进入Tab流的唯一合理方式,需同步添加语义角色、监听keydown事件并提供视觉焦点样式;tabindex="-1"用于编程聚焦而非隐藏;禁用正数tabindex以避免焦点顺序混乱。

tabindex="0" 是让非原生元素进 Tab 流的唯一合理方式
想让 <div>、<code><span></span> 或 <label></label> 这类默认不可聚焦的元素支持 Tab 导航,唯一推荐的做法是加 tabindex="0"。它不改变 DOM 顺序,只是把元素“补位”进浏览器默认的 Tab 流——焦点会按 HTML 出现位置自然落到它身上。
但光加属性远远不够,必须同步做三件事:
- 加上语义角色,比如
role="button"或role="checkbox",否则屏幕阅读器只会读“div”,不知道它是按钮 - 监听
keydown,对Enter和Space都触发等效操作(注意Space要调e.preventDefault(),否则页面会滚动) - 提供视觉反馈,用
:focus-visible或:focus写样式,不然键盘用户根本看不到焦点在哪
tabindex="-1" 不是隐藏开关,而是编程聚焦的入口
设 tabindex="-1" 后,元素无法被 Tab 键选中,但它依然能被 .focus() 主动聚焦,且 onfocus 事件照常触发。这是模态框、下拉菜单、折叠面板这类动态组件的焦点控制核心。
典型用法包括:
立即学习“前端免费学习笔记(深入)”;
- 模态框打开后,立即对第一个可操作项(如“确认”按钮)调
.focus(),该按钮需提前设tabindex="-1" - Accordion 展开后,把焦点移到新暴露的首项,而不是留在触发按钮上
- 关闭弹层后,焦点应回退到触发它的元素(比如原“编辑”按钮),可用
document.activeElement记录或传参保存
错误做法是把它当“禁用”用:比如给表单项加 tabindex="-1" 想绕过验证——它不影响提交,也不阻止 JS 访问,纯属误导。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
绝对不要用 tabindex="1"、tabindex="2" 这类正数
写 tabindex="1" 并不等于“第一个被 Tab 到”,它只表示“比所有 tabindex="0" 和无 tabindex 的元素优先”。多个正数之间按数值升序排,但同值时仍按 DOM 顺序走——所以一堆 tabindex="1" 的按钮,实际还是从上到下依次进入,和没设一样,还多了一层混乱。
更严重的问题是:
- 一旦页面混用
tabindex="1"、tabindex="2"、tabindex="0",焦点顺序就变成“正数升序 → DOM 顺序 → 0 值元素 → 负值忽略”,极易错乱 - 屏幕阅读器会把
tabindex="1"元素当作逻辑起点,但如果它在页脚,就会造成认知断层 - React/Vue 动态渲染时,DOM 插入顺序变化后,硬编码的正数
tabindex会直接失效,焦点跳转错乱
focus() 后元素看不见?scrollIntoView 得手动补上
element.focus() 只管聚焦,不管滚动。尤其在以下场景,焦点进了但用户完全看不到:
- 元素在
overflow: hidden的父容器内 - 页面有
position: sticky导航栏,遮挡了顶部区域 - 模态框内容高度超出视口,焦点落在底部按钮上
解决方法很简单:聚焦后立刻跟一句 element.scrollIntoView({ block: 'nearest' })。别依赖浏览器自动滚动——它不总是生效,尤其是 Safari 在移动端对非原生可聚焦元素的限制更严,没 scrollIntoView 就真“消失”了。


















