原生JS多级动画下拉菜单求助:求MIT许可库或代码修复
解决方案:原生JS多级动画下拉菜单
一、MIT许可的原生JS库推荐
如果不想自己折腾代码,这些原生JS库完全符合你的需求,均为MIT开源许可:
- DropMenu:轻量级(<5KB),支持多级嵌套菜单,自带平滑展开/收起动画,无需任何依赖,API简洁易定制。
- Vanilla-Menu:专注于原生JS的多级菜单实现,支持点击/hover触发,动画参数可自定义,文档齐全易上手。
- SimpleMultiLevelMenu:极简风格,核心功能聚焦多级下拉,动画流畅自然,代码结构清晰,适合需要深度自定义样式的场景。
二、现有代码修复方案(不修改HTML结构)
原代码存在事件重复绑定、高度计算逻辑混乱、动画过渡不统一等问题,以下是修复后的完整代码,完全保留你提供的HTML结构:
修复后的JavaScript代码
// 初始化子菜单状态 document.querySelectorAll('ul ul').forEach(subMenu => { subMenu.classList.add('sub-menu'); subMenu.style.maxHeight = '0'; subMenu.style.transition = 'max-height 0.3s ease-in-out'; }); // 事件委托绑定到顶级导航,避免重复绑定问题 document.querySelector('nav > ul').addEventListener('click', (e) => { e.preventDefault(); const targetLi = e.target.closest('li'); if (!targetLi) return; const currentSubMenu = targetLi.querySelector('ul'); if (!currentSubMenu) return; // 没有子菜单的项直接返回 // 收起当前父层级下的其他子菜单 const parentUl = targetLi.closest('ul'); parentUl.querySelectorAll('ul').forEach(menu => { if (menu !== currentSubMenu && menu.style.maxHeight !== '0') { menu.style.maxHeight = '0'; } }); // 切换当前子菜单的展开/收起状态 const isExpanded = currentSubMenu.style.maxHeight !== '0'; currentSubMenu.style.maxHeight = isExpanded ? '0' : `${currentSubMenu.scrollHeight}px`; });
优化后的CSS样式
a { display: inline-block; width: 100%; height: 100%; padding: 5px 10px; color: white; text-decoration: none; /* 去掉链接下划线,提升视觉体验 */ } nav ul, nav ul ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; overflow: hidden; } nav ul { width: 200px; background: dodgerblue; position: relative; } nav ul ul { background: rgb(13, 130, 141); margin-left: 15px; border-left: 1px dashed white; max-height: 0; /* 统一用maxHeight控制展开收起 */ transition: max-height 0.3s ease-in-out; /* 统一过渡动画 */ } nav ul ul ul { background: rgb(1, 8, 8); margin-left: 15px; border-left: 1px dashed white; } nav ul ul ul ul { background: rgb(10, 41, 179); margin-left: 15px; border-left: 1px dashed white; } /* 给带自菜单的项添加箭头提示,提升用户体验 */ li:has(> ul) > a { position: relative; padding-right: 25px; } li:has(> ul) > a::after { content: '▶'; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 12px; transition: transform 0.3s ease; } li:has(> ul[style="max-height: 0px;"]) > a::after { transform: translateY(-50%) rotate(-90deg); }
修复说明
- 事件委托优化:将事件绑定到顶级导航ul,避免给每个li重复绑定事件,性能更优且逻辑更清晰。
- 统一动画逻辑:抛弃原代码混用
min-height/max-height的方式,统一用max-height控制菜单展开收起,动画过渡更平滑稳定。 - 层级管理优化:点击展开子菜单时,自动收起当前父层级下的其他子菜单,避免多个菜单同时展开的混乱状态。
- 视觉体验提升:给带有子菜单的链接添加箭头标识,并随菜单状态切换箭头方向,用户操作更直观。
内容的提问来源于stack exchange,提问作者نور
相关产品推荐
相关产品推荐

