tabindex="0"是最简方式让任意元素支持tab导航,使其按dom顺序进入默认焦点流,不改变语义也不破坏顺序;但需配套处理键盘事件、视觉焦点样式及复杂组件的焦点管理。

tabindex="0" 是让任意元素支持 Tab 导航的最简方式
只要加 tabindex="0",<div>、<code><span></span>、<label></label> 这类默认不可聚焦的元素就能进入浏览器默认的 Tab 流,按 DOM 顺序被键盘选中。
这不是“黑魔法”,而是明确告诉浏览器:“这个元素需要参与标准焦点导航”。它不改变语义,也不打乱原有顺序——比用 tabindex="1" 或更高正数安全得多。
-
tabindex="-1"只能靠 JavaScript 主动调用.focus()聚焦,不能被 Tab 键到达 - 正数
tabindex(如tabindex="2")会强行插队,破坏用户预期的导航节奏,WCAG 和主流 a11y 指南都明确反对 - 原生可聚焦元素(
<button></button>、<a href></a>、<input>)无需额外加tabindex,加了反而可能干扰默认行为
光能 Tab 进去,不代表键盘操作就有效
用户按下 Enter 或 Space 时,浏览器不会自动触发点击;你得自己监听 keydown 并模拟行为。
常见错误是只绑 click 事件——这对鼠标用户没问题,但键盘用户根本触达不到。
- 必须监听
keydown,判断e.key === 'Enter'或e.key === ' '(注意空格键需e.preventDefault(),否则会滚动页面) - 避免用已废弃的
e.keyCode,统一用e.key或e.code - 如果用了
role="button"却没处理键盘事件,屏幕阅读器会报“该按钮不可操作”,实际按了也没反应
视觉焦点样式不能靠 outline: none 一删了事
很多项目全局清掉 outline,又没提供替代焦点指示,等于把键盘用户的路标抹掉。
WCAG 要求焦点状态必须清晰可见,且对比度 ≥ 3:1。直接删 outline 是高危操作。
- 推荐用
:focus-visible替代:focus,避免鼠标用户看到多余轮廓 - 示例:
:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; } - 如果组件本身有 hover/focus 状态色块,确保其满足对比度要求,不能仅靠边框粗细或颜色深浅“蒙混过关”
复杂交互组件(如选项卡、下拉菜单)要管理焦点流
单个元素加 tabindex="0" 只解决“进得来”,但用户进来了之后怎么在内部移动?怎么退出?这些必须由 JS 显式控制。
比如一个 role="tablist",Tab 进入第一个 tab 后,应支持方向键切换,Enter 或 Space 激活,Escape 关闭——这些不是浏览器默认行为。
- 用
tabindex="-1"控制内部可聚焦项(如 tabpanel 中的内容),避免 Tab 键跳出组件 - 模态框打开后,第一时间将焦点移到第一个可聚焦子元素,并限制 Tab 键只能在模态框内循环(即“焦点陷阱”)
- 动态插入内容(如搜索结果列表)后,若用户期望立即操作,需主动
.focus()到合适位置,而不是让用户手动 Tab 十几次
真正难的从来不是加 tabindex="0" 这一行代码,而是后续所有与之配套的键盘行为、视觉反馈和焦点流转逻辑——它们必须作为一个整体设计,而不是零散补丁。











