tabindex="0" 是让非原生可聚焦元素(如 div/span)参与键盘导航的必要起点,但需配合 role、keydown 监听和焦点样式才可用;禁用正数 tabindex 和滥用 tabindex="-1" 会破坏焦点流。

tabindex="0" 是让 div/span 可聚焦的唯一起点
直接给 <div> 或 <code><span></span> 加 tabindex="0",它才能被 Tab 键导航到——但这只是“开门”,不是“能用”。原生按钮按空格就触发,<div tabindex="0"> 按空格只会滚动页面,没监听、没语义、没样式,等于放了个假门把手。
<ul><li>只对非原生可聚焦元素使用:比如 <code><div>、<code><span></span>、<li>、<article></article>
<button></button>、<a href></a>、<input> 上——它们默认就在 Tab 流里,加了反而可能破坏 SSR/hydration 焦点同步tabindex="1" 或其他正数:它不会让你的元素变成“第一个被 Tab 到的”,只会制造焦点顺序混乱,尤其在 React/Vue 动态渲染中极易失效加了 tabindex="0" 还不能按 Enter/Space?缺这三样必卡死
键盘用户 Tab 进去之后,发现按回车没反应、按空格页面往下滚、屏幕阅读器读成“段落”——这是典型配套缺失。光有 tabindex="0" 只解决“能不能获得焦点”,不解决“获得焦点后怎么响应”。
- 必须加
role:比如<div role="button" tabindex="0">删除</div>,否则辅助技术不知道这是可操作项 - 必须监听
keydown:捕获Enter和Space,对Space调用e.preventDefault()(否则触发浏览器默认滚动) - 必须提供可见焦点样式:用
:focus-visible最稳妥;若只写:focus,鼠标用户会看到突兀轮廓,建议配合outline: none+ 自定义边框或阴影
表格行 tr 加 tabindex="0" 的坑比想象中多
<tr> 默认不可聚焦,加 <code>tabindex="0" 是让它参与键盘导航的必要操作,但仅此不够——移动端 Safari 完全不支持 <tr> 直接聚焦,DOM 中加了也白加。
<ul>
<li>必须用 <code>tabindex="0",禁用 -1 或正数:前者进不了 Tab 流,后者打乱顺序
<button></button> 放在 <td> 底部,别用 <code><div role="button"> 模拟
<li>移动端兼容方案:额外加一个 <code>position: absolute; opacity: 0; 的隐藏 <button tabindex="0"></button>,绑定相同逻辑,Safari 才认什么时候该用 tabindex="-1"?别和 "0" 混用
tabindex="-1" 和 "0" 完全是两套用途:前者是“禁止 Tab 键到达,但允许 JS 主动聚焦”,后者是“让 Tab 键能到达”。混用会导致焦点流断裂,比如模态框关闭后焦点丢失。
- 典型场景:模态框打开后,用
el.focus()聚焦到第一个输入框;表单校验失败后,把焦点移到首个错误字段 - 别给需要键盘导航的交互容器设
-1:比如自定义折叠面板标题、tab 标签页,它们得靠"0"进入 Tab 流 - 检查 DOM 中是否意外存在
tabindex="-1"的原生控件:比如<button tabindex="-1"></button>,这会让它彻底退出 Tab 流,键盘用户永远碰不到
tabindex="0",而是判断这个元素到底“值不值得被 Tab 到”——如果它只是视觉装饰、图标容器、或纯展示性文本,加了只会增加无效停靠点,拖慢键盘用户的操作节奏。











