直接用嵌套 + max-height过渡 + 媒体查询即可10分钟搭出可用响应式折叠菜单;html必须用语义化嵌套结构,禁用模拟,子须紧贴.toggle后,max-height过渡实现平滑展开,移动端需保障44×44px点击热区并真机测试三级兼容性。

直接用嵌套 <ul></ul> + max-height 过渡 + 媒体查询,就能在 10 分钟内搭出可用的响应式折叠菜单,不用框架、不依赖 jQuery。
HTML 必须用嵌套 <ul></ul> 构建语义结构
多级菜单不能靠 <div> 堆叠,否则屏幕阅读器读不出层级,键盘 Tab 会跳过子项。正确结构是:<pre class="brush:php;toolbar:false;"></pre>
<ul class="menu">
<li class="menu-item">
<span class="toggle">产品</span>
<ul class="nested">
<li><a href="#">手机</a></li>
<li><a href="#">平板</a></li>
</ul>
</li>
<li class="menu-item">
<span class="toggle">服务</span>
<ul class="nested">
<li><a href="#">技术支持</a></li>
</ul>
</li>
</ul>
<ul>
<li>
<code><ul class="nested"></ul> 必须紧贴在 <span class="toggle"></span> 后面,不能隔元素,否则 nextElementSibling 找不到
<li class="menu-item"> 都要包裹可点击区域(如 <span class="toggle"></span>),不能把点击逻辑绑在 <li> 上——会误触子项<details></details> 嵌套:Safari 对 <details></details> 内再套 <details></details> 支持极差,open 状态不同步
max-height 过渡比 display 更适合动画
display: none/block 无法过渡,硬切会闪;max-height 虽然要预估值,但能平滑展开。关键写法:
.nested {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
}
.menu-item.active .nested {
max-height: 240px; /* 按最多 6 个子项估算,每项行高 40px */
}
- 设太小(如
100px)会导致子项被截断,尤其文字换行后高度不可控 - 设太大(如
1000px)会让动画变慢,滚动时有明显延迟感 - 不要用
height: auto配 transition —— 浏览器不支持auto的过渡计算
移动端汉堡菜单和折叠菜单共存时的 CSS 切换逻辑
桌面端显示完整横向菜单,移动端收起主菜单、只留汉堡按钮,同时启用折叠逻辑。靠媒体查询分离:
@media (max-width: 768px) {
.nav-menu { display: none; }
.nav-toggle { display: block; }
.nav-menu.active { display: flex; flex-direction: column; width: 100%; }
/* 折叠菜单项在此宽度下才启用 toggle 行为 */
.menu-item .toggle { display: inline; }
.menu-item .nested { margin-left: 1rem; }
}
-
.nav-menu和.menu是两个独立容器:前者是顶部导航栏(含首页/关于我们等),后者是侧边或下拉树形菜单,别混用同一套 class - 小屏下若同时存在汉堡菜单和内部折叠项,需确保
.toggle在移动端有足够点击热区(至少 44×44px),否则 iOS Safari 容易点不中 - 别在
@media里重写transition属性——容易覆盖掉折叠动画,统一写在默认样式里更稳
真正麻烦的不是写代码,而是当菜单嵌套到第三级时,:has(> .nested) 在旧版 Edge 或 Firefox 68 以下不支持,得 fallback 到 JS 判断 element.querySelector('.nested') 是否存在;这个兼容性缺口常被忽略,上线前务必真机测三级展开。











