多级联动菜单展开按钮不能用嵌套,应采用input[type="checkbox"]+label+~纯css方案;dom顺序须严格:input第一、label紧随、ul为label直接兄弟;css用:checked ~ .submenu控制显示,移动端重置display:none;:has(> .submenu)隔离层级防穿透;事件委托绑定父容器click,ios需touchstart preventdefault。

多级联动菜单的展开按钮不能靠 <details></details> 嵌套实现——iOS Safari 和微信 X5 内核下必然失效,状态不同步是通病,不是偶发。
用 input[type="checkbox"] + label + ~ 实现纯 CSS 展开按钮
这是目前最稳的无 JS 方案,能记住状态、兼容触摸、语义完整。关键在 DOM 顺序和选择器写法:
-
<input type="checkbox" id="menu-1">必须是第一个元素,不能有空格或换行 -
<label for="menu-1">Products</label>必须紧接其后,中间不能插入任何节点(包括文本节点) -
<ul class="submenu">...</ul>必须是label的**直接兄弟节点**,不能嵌套在别的容器里 - CSS 规则必须写成:
input[type="checkbox"]:checked ~ .submenu { display: block; },不能用visibility或opacity - 移动端需在
@media (max-width: 768px)中重置所有.submenu为display: none,不依赖 JS 切换
:has(ul) + > 子选择器控制层级隔离
避免“一点全开”问题的核心是切断 CSS 后代穿透。原生 :has() 支持现代浏览器,降级可加 class="has-child":
-
.menu-item:has(> .submenu) > .toggle::after只匹配直接含子菜单的项,不穿透到孙子级 -
.menu-item.active > .submenu确保只展开下一级,不会误触深层嵌套 - IE 不支持
:has(),但可用 JS 在初始化时补上has-child类,CSS 保持一致 - 别写
.active ul—— 这会让所有后代<ul></ul>一起显示,破坏独立折叠逻辑
JS 事件委托绑定 click,不重复监听每个菜单项
动态增删菜单时,单独给每个 <li class="menu-item"> 绑 addEventListener 极易漏绑或重复绑定:
- 统一监听父容器(如
<ul class="menu"></ul>)的click事件 - 用
event.target.closest(".menu-item")获取点击项,再判断它是否含.toggle或:has(> .submenu) - 切换类名时只操作当前项的
active,不 touch 兄弟或祖先节点 - iOS 需额外处理
touchstart并调用preventDefault(),否则会触发两次click
真正难的不是让菜单动起来,而是确保每层展开状态彼此隔离、键盘导航不跳级、小屏下不挤压内容区——这些细节一旦忽略,用户在微信里点开二级菜单时,一级就自动收起,根本没法用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











