能,需用 radio/checkbox 的 :checked 状态配合 + 或 ~ 选择器控制面板显隐:input 隐藏且同 name,label 绑定 id,面板紧随其后,首个 input 加 checked,CSS 通过 #id:checked ~ .panel 设置 display:block。

怎么用纯HTML+CSS实现选项卡,不写JavaScript
能,但有严格限制:切换行为必须依赖 <input type="radio"> 或 <input type="checkbox"> 的状态变化,再用相邻兄弟选择器(~)或通用兄弟选择器(+)联动显示/隐藏面板。浏览器原生不支持纯HTML的“点击切换”逻辑,所谓“纯HTML”实际是“HTML + CSS + 表单控件伪交互”。
常见错误现象:click 无反应、面板不切换、回退时状态丢失、键盘无法操作(如 Tab 切换后按 Space 不触发)。
- 必须给每个
<input>设置相同的name(对radio是必需的),否则单选失效 -
<label>必须通过for属性绑定到id,或直接包裹<input>,否则点击无效 - 面板元素必须紧跟在对应
<input>后面,或在其后同级位置,否则~选择器无法命中 - 默认第一个选项卡要可见,需给首个
<input>加checked,且对应面板不能设display: none初始态
CSS里怎么控制面板显隐——关键在 :checked 和兄弟选择器
核心原理是:利用 :checked 伪类匹配已选中的表单控件,再用 +(紧邻兄弟)或 ~(后续所有兄弟)定位到目标面板。不能用父选择器,所以结构必须是“控件 → 标签 → 面板”,且面板不能嵌套在标签内部。
使用场景:文档页签、设置分组、FAQ 折叠区(单选)、多图切换(单选)。
立即学习“前端免费学习笔记(深入)”;
性能影响几乎为零,兼容性极好(IE9+ 支持 :checked,IE8 需降级为 JS)。
示例结构片段:
<input type="radio" name="tab" id="tab1" checked> <label for="tab1">首页</label> <div class="panel">这里是首页内容</div> <input type="radio" name="tab" id="tab2"> <label for="tab2">关于</label> <div class="panel">这里是关于内容</div>
CSS 关键句:
input[name="tab"] { display: none; }
.panel { display: none; }
#tab1:checked ~ .panel:nth-of-type(1),
#tab2:checked ~ .panel:nth-of-type(2) { display: block; }
为什么不用 <details><summary> 做选项卡
<details> 天然支持展开/收起,语义正确,键盘友好(Enter/Space 可触发),但它是**单个可折叠区块**,不是“互斥多选一”的选项卡。多个 <details> 并列时,可以同时展开多个,不符合典型选项卡行为(比如点“用户”就该收起“设置”)。
容易踩的坑:
- 强行用 JS 控制多个
<details>的open状态,那就不是“纯HTML”了 -
<details>不支持 CSS 选择器定位“当前展开项”,无法用纯CSS做样式联动(比如高亮当前标题) - 部分旧版 Safari 对
<details>的 CSS 过渡支持不稳定
移动端和可访问性要注意什么
纯CSS选项卡在移动端基本可用,但触摸反馈弱——没点击波纹、没状态提示。屏幕阅读器能读出 label 和面板内容,前提是结构正确、aria-hidden 没乱加。
必须检查的点:
- 每个
<label>的文本要准确描述面板内容,别写“点击切换”这种废话 - 面板区域建议加
role="tabpanel",<label>加role="tab"和aria-controls指向面板ID(否则VO/NVDA可能无法建立关联) - 不要给
<input>设visibility: hidden或opacity: 0,应始终用position: absolute; left: -9999px移出视口,保证可聚焦、可键盘操作 - 如果面板内容较多,首次加载时所有面板都渲染了,SEO 和首屏性能不受影响,但内存占用略高(相比JS懒加载)
最麻烦的其实是焦点管理:用户用Tab进入面板后,按Shift+Tab会回到上一个 <input>,而不是前一个标签,这个逻辑没法用CSS修正——真要严谨,还是得加少量JS接管 keydown。











