应避免使用正整数 tabindex 控制网格焦点顺序,因其会打乱视觉与逻辑顺序;正确做法是用 tabindex="0" 使自定义控件按 DOM 顺序进入焦点流,tabindex="-1" 仅用于 JS 动态聚焦,Grid 需配合 ARIA role 显式声明结构。

别用 tabindex="1" 控制网格焦点顺序——它不会按你看到的行/列走,只会把元素全塞到 Tab 流最前面,然后随机跳。
为什么 grid 布局里加 tabindex="1" 必然错乱
浏览器对正整数 tabindex 的处理是:所有 tabindex="1"、tabindex="2" 等元素被统一提到整个 Tab 序列开头,再按数值升序排;同值时仍回退到 DOM 插入顺序。但 Grid 布局本身不改 DOM 顺序,视觉上左上角的按钮可能在 HTML 里是第 12 个节点。
常见后果包括:
- 页脚的“导出”按钮设了
tabindex="1",用户一进页面就 Tab 到它,主编辑区完全被跳过 - React 动态渲染后,同一组
tabindex="1"按组件挂载时机不同,焦点顺序今天从上到下、明天从左到右 - 屏幕阅读器把第一个
tabindex="1"当作逻辑起点,播报时丢失上下文(比如只读“导出”,不读“当前报表页脚”) - 原生可聚焦元素(如
button、input)被挤到所有正数之后,Tab 流断层
tabindex="0" 该加在哪,怎么配才有效
tabindex="0" 不是排序指令,只是让非原生可聚焦元素(如 div、article)按 DOM 顺序进入焦点流。但它必须配合语义和交互逻辑才能真正可用。
立即学习“前端免费学习笔记(深入)”;
只对以下情况加 tabindex="0":
- 自定义控件容器,例如
<div class="card-header" role="button">,且内部没有原生button - 折叠面板标题、选项卡标签(
role="tab")、可展开区域(role="region") - 必须同步设置
role属性,否则屏幕阅读器读作“div”而非“按钮”或“标签” - 必须监听
keydown,对Enter和Space触发操作;Space要调e.preventDefault(),否则滚动页面 - 必须提供
:focus-visible样式,否则键盘用户看不见焦点在哪
别给纯展示项(如头像 div、图标 span)加 tabindex="0",这只会增加无效停靠点。
tabindex="-1" 是动态聚焦的唯一入口,不是隐藏开关
tabindex="-1" 的作用非常窄:禁止 Tab 键到达,但允许 JS 主动 .focus()。它在网格中几乎只用于两种场景:模态子面板首次打开、折叠区域展开后聚焦首项。
实操要点:
- 目标元素(如新弹出侧边栏里的第一个
input)必须在 HTML 中就带tabindex="-1",不能等 JS 渲染完再加——否则.focus()失效或静默忽略 - 不要给整个网格容器或面板外层
div加tabindex="-1",否则.focus()落在空容器上,用户按 Tab 还是跳出组件 - 确保 DOM 已渲染完成再调
.focus(),可用requestAnimationFrame或setTimeout(() => el.focus(), 0) - 聚焦后若元素被
overflow: hidden、transform或position: absolute截断,必须补el.scrollIntoView({ block: 'nearest' }) - 关闭动态区域时,记得把焦点退回到触发它的原始元素(可用
document.activeElement记录或事件参数传递)
Grid 容器和单元格的 role + tabindex 组合规则
仅写 display: grid 的容器在无障碍层面等于 div:无标题、无行列索引、无单元格关联。必须用 ARIA 显式声明结构。
正确组合方式:
- 网格外层容器设
role="grid"+tabindex="-1"(仅语义声明,不参与 Tab 流) - 每行设
role="row",不加tabindex(避免干扰行内单元格聚焦顺序) - 每个单元格设
role="gridcell",内部可交互元素(如input type="radio"、button)保留原生聚焦能力,不要包裹在tabindex="-1"的div里 - 多维表格中,每个单选按钮必须同时用
aria-labelledby="row-id col-id"关联行头和列头 ID,缺一不可;顺序影响播报先后(通常行在前、列在后) - 列标题必须显式带
id,不能只靠scope="col";空白表头需用aria-label补充,否则关联后播报为空
真正决定网格导航体验的,不是 tabindex 数值,而是 DOM 结构是否贴近语义流、JS 是否在状态切换时精准接管焦点、CSS 是否避免用 opacity: 0 或 position: absolute 隐藏却未阻断聚焦。



















