默认可Tab到的元素包括<button>、、<input>、<select>、<textarea>;非原生交互容器如<div role="button">需加tabindex="0"并满足role、键盘事件、focus样式三条件;<area>不可聚焦,须用绝对定位的<button>/替代;SVG/Canvas热区需设tabindex="-1"并JS管理焦点;隐藏元素仅display: none或aria-hidden="true"且无tabindex才脱离Tab流。

哪些元素默认能被 Tab 到,哪些必须手动加 tabindex
原生语义元素如 <button>、<a>、<input>、<select>、<textarea> 默认就在 Tab 流中,不需要也不应该加 tabindex="0"。加了反而可能在 SSR/hydration 场景下导致焦点错位,或干扰 disabled 状态的自然行为。
只有视觉上可交互但非原生聚焦的容器才需要加 tabindex="0",比如:<div role="button">、自定义 tab 标签、卡片式操作区等。但必须同步满足三个条件:
-
role属性明确(如role="button"或role="link") - 监听
keydown并处理Enter和Space(注意Space需event.preventDefault()防止滚屏) - 提供
:focus-visible样式,且不能被全局* { outline: none }覆盖
图片地图(<map>)里的区域怎么支持键盘访问
<area> 元素本身**不可聚焦**,即使设置了 tabindex="0" 也无效——浏览器不支持对 <area> 调用 .focus(),也无法通过 Tab 键进入。这是规范限制,不是 bug。
要让图片地图区域可键盘访问,唯一可行路径是放弃纯 <map> 方案,改用语义化替代:
立即学习“前端免费学习笔记(深入)”;
- 用
<button>或<a>叠在图像上方,配合position: absolute和精确坐标定位 - 每个按钮/链接需有
aria-label描述其功能(如aria-label="查看产品详情"),不能只靠视觉位置暗示 - 确保所有叠加元素按逻辑顺序排列在 DOM 中,避免
z-index错乱导致焦点跳过 - 禁用
<img usemap>的href属性,防止点击穿透冲突
动态生成的可点击区域(如 SVG 图标组、Canvas 热区)如何接管焦点
SVG 内部的 <g>、<path> 或 Canvas 绘制的热区,默认完全不在焦点流里。想支持键盘导航,必须走 JS 主动聚焦路径,且目标元素需设 tabindex="-1"。
关键步骤包括:
- 为每个可交互 SVG 元素添加
tabindex="-1"和role="button"(或对应语义) - 监听容器的
focusin事件,用container.contains(document.activeElement)判断焦点是否还在内部 - 方向键导航时,缓存当前焦点索引,避免每次重新遍历 DOM;
ArrowDown/ArrowRight切到下一个,Home/End直接跳首/末项 - 模态弹出或面板展开后,立即对第一个可聚焦子项调用
.focus({ preventScroll: true });Safari 不支持该选项,需先检测:if ('preventScroll' in FocusOptions.prototype)
隐藏或禁用的可点击区域仍被 Tab 到?检查这些地方
仅用 opacity: 0、visibility: hidden、position: absolute; left: -9999px 隐藏的元素,依然会被 Tab 键捕获。真正脱离焦点流的方式只有两种:
-
display: none—— 元素从渲染树移除,focus()会失败,Tab 键自动跳过 -
aria-hidden="true"+ 移除tabindex—— 仅适用于纯装饰性区域,且必须确保无交互逻辑残留
特别注意:禁用状态(如 disabled 的 <button>)天然不进 Tab 流,但用 div[aria-disabled="true"] 模拟禁用时,必须同时移除 tabindex="0",否则用户仍会 Tab 到一个“看起来不能点”的东西上。



















