元素默认无焦点,仅a、button、input等原生交互元素天然可聚焦;div等需tabindex="0"+role+aria-*才具备完整可访问性,且须配可见焦点样式。

元素默认没有焦点,tabindex 是唯一可控的入口——但乱设会直接破坏可访问性。
哪些元素天然能获得焦点
不是所有标签点一下就能按 Tab 进入。只有少数原生可交互元素自带焦点能力:a(带 href)、button、input、select、textarea、iframe。其他如 div、span、li 默认完全“不可聚焦”,键盘用户根本碰不到。
常见错误现象:div 里写了个“点击展开”逻辑,加了 click 事件,却没处理键盘回车/空格触发,屏幕阅读器用户卡死。
- 别指望靠 JS 模拟点击就解决可访问性问题
- 如果语义上它是个按钮,就用
button,而不是给div加tabindex="0"再绑事件 - 若必须用非交互标签承载交互行为,
tabindex="0"是起点,但还得手动加keydown监听Enter和Space
tabindex 的三个取值怎么选
取值不是越小越好,也不是越大越强。它控制的是“能否聚焦”和“聚焦顺序”两个维度。
立即学习“前端免费学习笔记(深入)”;
-
tabindex="-1":不能通过 Tab 进入,但可用 JS 调用.focus()主动聚焦(比如模态框打开后把焦点移到第一个输入框) -
tabindex="0":加入自然 Tab 顺序,顺序由 DOM 位置决定;最常用,也最安全 -
tabindex="1"或更大正数:强制插入 Tab 序列前端,但会打乱阅读流,导致键盘用户反复跳转,已基本被 WCAG 不推荐
性能影响极小,但兼容性要注意:tabindex="-1" 在 IE8+ 支持,tabindex="0" 全兼容;正数 tabindex 在老 Android WebView 中偶有顺序错乱。
为什么 tabindex="0" 后还要加 role 和 aria-*
加了 tabindex="0" 只解决了“能聚焦”,没解决“是什么”和“怎么用”。屏幕阅读器靠 ARIA 理解语义。
- 一个可展开的
div加了tabindex="0",必须同时加role="button"或role="region"+aria-expanded - 缺少
aria-label或aria-labelledby,屏幕阅读器只会读“可聚焦元素”,不告诉你这是“搜索按钮”还是“关闭弹窗” - 动态改变状态(如折叠/展开)时,必须同步更新
aria-expanded值,否则辅助技术看到的是过期信息
示例:<div tabindex="0" role="button" aria-label="打开用户菜单" aria-expanded="false">⋯</div>
容易被忽略的 CSS 焦点样式问题
加了 tabindex,但用户按 Tab 到那儿却看不见焦点在哪——这比没加还糟,等于主动隐藏导航路径。
- 浏览器默认焦点框(outline)可能被全局 CSS 清掉,比如
*:focus { outline: none; },必须替换成可见的替代样式 - 用
:focus-visible更精准:只在键盘触发焦点时显示样式,鼠标点击时不干扰 - 颜色对比度要 ≥ 4.5:1,否则低视力用户根本看不到焦点环
别只测 Chrome,Safari 对 :focus-visible 支持晚,Firefox 需要前缀;真要兼容,:focus + JS 检测 keyboard-focus 类更稳。
真正的难点不在怎么写 tabindex,而在于每次加之前,得先问一句:这个元素在语义上到底该不该被聚焦?如果答案是否定的,再漂亮的 JS 聚焦逻辑也是补丁。











