支持,但需同 name 组且无 disabled/tabindex="-1" 干扰;Tab 进入首项,方向键组内切换,再次 Tab 才离开;原生不支持同组 Tab 循环,需 JS 实现但慎用。

radio 单选框默认支持 Tab 切换吗?
支持,但有前提:所有 必须属于同一 name 组,且至少有一个没有被 disabled 或 tabindex="-1" 干扰。浏览器原生行为是——Tab 进入该组第一个可聚焦的 radio,之后按方向键(←→ 或 ↑↓)在组内切换;再次 Tab 才跳到下一个可聚焦元素。
注意:Tab 不会在同组 radio 间“循环切换”,它只进不出(除非你手动加 tabindex 控制流)。
为什么按 Tab 后只能选中第一个 radio,方向键没反应?
常见原因如下:
- 页面用了
event.preventDefault() 拦截了 keydown 事件(尤其在全局监听或 UI 库里)
- radio 被包裹在
role="radiogroup" 容器中,但缺少必要的 ARIA 属性(如 aria-activedescendant),导致辅助技术接管了焦点逻辑,反而禁用了原生方向键
- CSS 中设置了
outline: none 且没提供替代焦点样式,让人误以为“没切过去”——其实已聚焦,只是看不见
- 某些框架(如 Vue + Element UI)会把原生 radio 替换为自定义组件,此时原生键盘行为完全失效
如何确保 Tab + 方向键正常工作?
最稳妥的做法是**不破坏原生语义**:
- 用标准 HTML 写法:
、
- 避免给 radio 设置
tabindex="0" 或 tabindex="-1"(除非你真要重定义焦点顺序)
- 检查父容器是否意外加了
tabindex="-1" 或 inert 属性
- 用 DevTools 的 Elements 面板确认:切换时,哪个 radio 的
:focus 状态被触发(右键 → “Break on → attribute modifications” 可辅助定位干扰逻辑)
需要 Tab 循环切换同组 radio 怎么办?
原生不支持 Tab 在同组内循环,但可通过 JS 补充:
document.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
const radios = document.querySelectorAll('input[type="radio"][name="theme"]');
const focused = document.activeElement;
if (!focused || !radios.includes(focused)) return;
<p>const idx = Array.from(radios).indexOf(focused);
let nextIdx = e.shiftKey ? (idx - 1 + radios.length) % radios.length
: (idx + 1) % radios.length;</p><p>radios[nextIdx].focus();
e.preventDefault();
});</p>
event.preventDefault() 拦截了 keydown 事件(尤其在全局监听或 UI 库里)role="radiogroup" 容器中,但缺少必要的 ARIA 属性(如 aria-activedescendant),导致辅助技术接管了焦点逻辑,反而禁用了原生方向键outline: none 且没提供替代焦点样式,让人误以为“没切过去”——其实已聚焦,只是看不见- 用标准 HTML 写法:
、 - 避免给 radio 设置
tabindex="0"或tabindex="-1"(除非你真要重定义焦点顺序) - 检查父容器是否意外加了
tabindex="-1"或inert属性 - 用 DevTools 的 Elements 面板确认:切换时,哪个 radio 的
:focus状态被触发(右键 → “Break on → attribute modifications” 可辅助定位干扰逻辑)
需要 Tab 循环切换同组 radio 怎么办?
原生不支持 Tab 在同组内循环,但可通过 JS 补充:
document.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
const radios = document.querySelectorAll('input[type="radio"][name="theme"]');
const focused = document.activeElement;
if (!focused || !radios.includes(focused)) return;
<p>const idx = Array.from(radios).indexOf(focused);
let nextIdx = e.shiftKey ? (idx - 1 + radios.length) % radios.length
: (idx + 1) % radios.length;</p><p>radios[nextIdx].focus();
e.preventDefault();
});</p>
⚠️ 注意:这段代码会覆盖原生 Tab 行为,务必只对明确需要循环的场景使用;同时要配合 keydown 和 keyup 做防抖,否则快速连按 Tab 可能导致焦点错乱。
实际项目里,多数无障碍需求并不要求 Tab 循环——方向键才是 radio 组的标准导航方式。强行改 Tab 行为反而会让屏幕阅读器用户困惑。真正该花时间的是确保每组 radio 有清晰的 或 aria-labelledby,以及 focus 样式可见。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











