可用 input[type="radio"] 实现无 JS 选项卡,利用 name 互斥与 :checked 伪类联动,所有内容保留在 DOM 中,通过 CSS 视觉隐藏而非 display: none 来保障可访问性。

用 input[type="radio"] 实现无 JS 的选项卡切换
纯 HTML/CSS 能做选项卡,关键不是靠 div 显隐控制,而是利用单选按钮的互斥特性和 :checked 伪类联动。浏览器原生支持,零 JS,兼容性好(IE9+),且天然可访问。
常见错误是直接套用 display: none + JS 切 class,结果 SEO 友好性差、首屏内容被隐藏、键盘导航失效。而 radio 方案让所有 tab 内容始终在 DOM 中,仅靠 CSS 控制可见性。
- 每个
input[type="radio"]必须有相同name值,才能互斥选择 - 对应面板用
id,radio 的value或id需与面板id一致(便于 CSS 关联) - 用相邻兄弟选择器
input:checked + .tab-panel或属性选择器[data-tab="xxx"]:checked ~ [data-target="xxx"]控制显隐 - 默认选中项靠给某个
input加checked属性,别忘了加tabindex="0"保证键盘可聚焦
label 必须包裹或关联 input,否则点击无效
很多人写 <label>Tab1</label><input type="radio" name="tab">,这会导致点击文字不触发选中——label 和 input 没建立绑定关系。
正确做法只有两种:label 包裹 input,或用 for 属性指向 input 的 id。后者更灵活,适合样式分离:
立即学习“前端免费学习笔记(深入)”;
<input type="radio" name="tab" id="tab1" checked> <label for="tab1">首页</label> <div class="tab-panel" id="panel1">...</div>
注意:for 的值必须和 input 的 id 完全一致,大小写敏感,不能有空格或特殊字符。
CSS 显隐控制要避开 display: none 的可访问性陷阱
如果用 display: none 隐藏未选中的面板,屏幕阅读器会跳过这些内容,用户无法通过辅助技术访问。应该改用视觉隐藏但保留语义的方式:
- 推荐组合:
position: absolute; clip: rect(0 0 0 0); width: 1px; height: 1px; margin: -1px; overflow: hidden; - 或者用
visibility: hidden; position: absolute;(需确保父容器position: relative) - 绝对禁止对非活跃面板设
display: none或aria-hidden="true"(除非你手动管理焦点和状态) - 选中面板的显示样式建议用
position: static; clip: auto; width: auto; height: auto;还原
移动端点击区域太小?用 ::before 扩大热区
原生 radio 按钮本身很小,尤其在手机上容易点不准。不要给 input 加宽高,而是用伪元素扩展 label 的点击范围:
label {
position: relative;
padding: 12px 20px;
}
label::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
z-index: -1;
}
这样整个 label 区域都可点击,且不影响 radio 默认样式。如果需要自定义选中态图标,也建议用 label::after + input:checked + label::after 控制,而不是覆盖 input 本身。
最易被忽略的是:radio 选项卡在 Safari iOS 上偶尔有点击延迟或失效,根源常是父容器设置了 overflow: hidden 或 -webkit-overflow-scrolling: touch,检查并移除即可。



















