子菜单不展开主因是事件冒泡失控,应仅在折叠按钮上用stoppropagation;css动画改用transform: scaley()提升性能;移动端优先用touchstart+preventdefault或fastclick防点击穿透;多级菜单宜用数据驱动+map缓存优化;须保障键盘可访问性。

点击后子菜单不展开?检查 event.stopPropagation() 是否误杀
很多同学加了点击事件,但子菜单始终不显示,大概率是父级菜单的点击处理里没控制好事件冒泡。电商侧边栏常见结构是 <li> 套 <ul></ul>,点击子项时如果父 <li> 也绑了 toggle 逻辑,就会互相干扰。
- 只在触发折叠/展开的按钮(比如
<button class="toggle"></button>)上加event.stopPropagation(),别直接绑在<li>上 - 用
event.target判断点击是否落在折叠按钮上,而不是监听整个菜单项 - Vue/React 用户注意:模板中写
@click.stop或onClick={(e) => { e.stopPropagation(); ... }}更安全
CSS 层级展开动画卡顿?避免对 height 做过渡
直接给 ul 设置 transition: height 0.3s 看似简单,但浏览器要反复计算内容高度,尤其子项多或含图片时,容易掉帧甚至闪动。
- 改用
transform: scaleY()+overflow: hidden,性能更好,且支持硬件加速 - 初始状态设为
transform: scaleY(0); opacity: 0;,展开时改为scaleY(1); opacity: 1; - 别忘了给父容器加
transform: translateZ(0)或will-change: transform触发 GPU 渲染
移动端点击穿透?touchstart 替代 click 是权宜之计
在 iOS Safari 或某些安卓 WebView 里,快速点击分类项后跳转到商品页,但下级菜单一闪就消失——这是典型的 300ms 延迟导致的点击穿透(click-through)。
- 优先用
touchstart,但必须配合preventDefault()和手动判断点击有效性(比如防止滑动误触) - 更稳妥的做法是引入
fastclick库,或用现代方案:在加<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> - 真机测试时注意:Chrome DevTools 的「Toggle device toolbar」模拟不完全等价于真实触摸行为,务必连真机调试
多级嵌套时 JS 递归控制混乱?用数据驱动代替 DOM 遍历
侧边栏层级深(比如「女装 > 连衣裙 > 碎花 > 春夏款」),如果靠 querySelectorAll('ul ul ul') 找节点再逐个 show/hide,代码难维护,还容易漏状态。
立即学习“前端免费学习笔记(深入)”;
- 把菜单结构存成扁平数组,每个项带
id、parentId、level字段,展开逻辑只操作数据,再用一次渲染更新 DOM - 用
Map缓存父子关系:比如const childrenMap = new Map(); childrenMap.set(parentId, [child1, child2]),查子集 O(1) - 避免在事件里反复调用
getBoundingClientRect()或offsetHeight,这些会强制同步回流
Tab 键进不到子菜单,或 Enter / Space 无法触发展开。这不只是合规问题,盲人用户和习惯键盘操作的开发者都会卡住。加 tabindex="-1" 和 aria-expanded 不难,但得从第一版结构就想着它。










