纯CSS折叠菜单核心是:checked + ~选择器联动,通过隐藏checkbox状态控制.menu的max-height与overflow实现动画,需注意ID匹配、overflow:hidden、固定max-height值、媒体查询覆盖、aria-expanded同步及IE/Safari兼容性。

用 :checked + ~ 选择器控制菜单显隐
核心不是 JavaScript,而是利用 checkbox 的状态和兄弟选择器联动 DOM。checkbox 本身隐藏,但它的 :checked 状态能触发后续元素样式变化,比如把 .menu 从 max-height: 0 改成有值,再配合 overflow: hidden 和 transition 实现收放动画。
常见错误是没加 for 属性或 ID 不匹配,导致点击 label 不触发 checkbox;或者忘了给折叠容器设 overflow: hidden,内容直接撑开、动画失效。
-
必须有唯一id -
的for值必须严格等于该id - 菜单容器要用
~(后续兄弟选择器),不能用+(紧邻兄弟),因为 label 和 menu 之间常有其他元素(如 logo) - 动画依赖
max-height,但不能设为max-height: none—— 它无法过渡,得用足够大的固定值,比如max-height: 500px
移动端适配:媒体查询里重置 checkbox 状态
PC 端可能默认展开,移动端才需要折叠。这时候不能靠 JS 切换 checked,而是在 CSS 里用媒体查询「覆盖」初始状态。关键是:小屏下让 checkbox 默认不选中,大屏下用 @media (min-width: 768px) 强制设为 :checked 并显示菜单。
容易踩的坑是只写了小屏逻辑,忘了大屏下要主动“解除折叠”——否则菜单在桌面端也保持收起,用户看不到导航。
- 默认写法应面向移动优先:
#nav-toggle { display: none; }+ 小屏下菜单max-height: 0 - 桌面端媒体查询里加:
#nav-toggle:checked ~ .menu { max-height: 500px; },同时确保.menu在桌面端有正常布局(比如display: flex) - 不要用
visibility: hidden替代max-height—— 它不触发重排,动画会卡顿或不生效
无障碍与可访问性绕不开 aria-expanded
纯 CSS 方案对屏幕阅读器不友好:checkbox 状态变了,但菜单区域没同步语义。用户无法知道当前是“已展开”还是“已折叠”。必须手动用 JS 补上 aria-expanded 属性切换,否则 WCAG 至少 fail 4.1.2。
这不是“锦上添花”,而是实际使用中会被读屏软件反复询问“这个按钮控制什么?现在状态如何?”的关键信息。
- 监听
#nav-toggle的change事件,而不是click - 切换时更新:
menuEl.setAttribute('aria-expanded', toggleEl.checked) - 初始状态也要同步设置一次,避免首屏加载后属性缺失
- 别只改
aria-expanded,记得同步aria-hidden到菜单内部(如果用了display: none)
IE 兼容问题:~ 和 transition 的底线
IE9+ 支持 ~,但 IE10 才开始支持 max-height 过渡;IE9 只能做硬切(无动画)。如果必须兼容 IE9,就放弃过渡,改用 display: block/none + JS 控制,或者直接放弃 checkbox 方案、改用 class 切换。
另一个隐形坑是 Safari 旧版对 max-height 过渡的渲染异常:高度会跳变,尤其内容动态加载后。解决方案是给菜单加 will-change: max-height 或强制触发布局(如 transform: translateZ(0))。
- 检查目标浏览器支持表:
~(IE9+)、transition(IE10+)、max-height动画(IE10+) - Safari 13.1 以下对
max-height: 0 → auto过渡完全无效,必须用固定值 - 别在
@keyframes里试图动画height: auto—— 浏览器不认,一律当height: 0
事情说清了就结束。真正难的不是写出能动的菜单,而是让每一次点击都符合预期:视觉反馈准、键盘可操作、读屏能理解、老浏览器不崩。这些点串起来,才是一个能上线的响应式折叠菜单。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











