tabindex合法值仅0和-1,正整数(如"1")会打乱焦点流、触发hydration警告、违反WCAG;tabindex="0"仅用于需键盘访问的非原生可聚焦元素并配role与事件;tabindex="-1"专供JS编程聚焦,须确保元素已挂载且未隐藏。

tabindex 的合法值只有 0 和 -1,所有正整数(如 tabindex="1")必须删除——它们不按你设想排序,反而会切断焦点流、触发 hydration 警告、让屏幕阅读器用户迷失上下文。
为什么 tabindex="1" 是最危险的写法
它不会让元素变成“第一个被 Tab 到的”,而是把所有正数项提前挤到整个 Tab 流最前面,再按数值升序排;同值时退回到 DOM 顺序。结果往往是:页脚按钮比主表单还先被聚焦,中间输入框全被跳过。
- 浏览器只认最小正整数(比如多个
tabindex="3",只取第一个生效),其余全忽略 - React/Vue 动态渲染或 SSR 场景下,这个值根本不可维护——DOM 插入新字段后顺序全乱
- WCAG 明确反对使用正整数 tabindex,因为这直接破坏线性导航逻辑
tabindex="0" 该加在哪些元素上
仅加在默认不可聚焦、但业务上必须支持键盘访问的容器上,比如 <div role="button">、<h3 role="tab">、自定义折叠面板标题栏。
- 原生可聚焦元素(
<button>、<input>、<a href>)加tabindex="0"是冗余,还可能干扰 hydration - 加了之后必须同步提供
role(如role="button")、监听keydown(处理'Enter'和' '),并设置:focus-visible样式 - 纯展示型元素(图标、
<span>、<svg>)加了只会制造无效停靠点,应直接移除
tabindex="-1" 不是隐藏开关,而是编程聚焦入口
它的唯一合法用途是:让 JS 能调用 .focus(),但用户无法通过 Tab 键到达。模态框打开后焦点没进到第一个按钮?大概率是目标按钮没提前设 tabindex="-1"。
立即学习“前端免费学习笔记(深入)”;
- 对非原生元素(如
<div role="button">),tabindex="-1"是.focus()成功的前提 - 移动端 Safari 在无用户手势上下文中(如 Promise.then 里)调用
.focus()会静默失败,必须在 click/keydown 回调中立即执行 -
display: none或visibility: hidden的元素即使有tabindex="-1",.focus()也无效
动态组件中 .focus() 失效的根本原因
不是 JS 写错了,而是目标元素没准备好可聚焦状态:DOM 未挂载、被隐藏、异步内容未就绪,或 SPA 中引用已失效。
- React/Vue 中别闭包里存
document.querySelector()返回的节点引用——组件重渲染后节点可能已被 detach - 稳妥做法是存 ID(如
dialog.dataset.triggerId = 'edit-btn'),关闭时再查document.getElementById() - 确保渲染完成再聚焦:用
requestAnimationFrame(() => el.focus())或监听dialog:open(需 polyfill)



















