JavaScript实现多级下拉导航菜单需用事件委托监听父容器,点击带has-submenu的链接切换相邻.submenu的show类;外部点击收起所有;CSS控制display/opacity过渡;键盘支持Enter/Space展开、Escape收起,并同步aria-expanded状态。

用 JavaScript DOM 操作实现多级下拉导航菜单,核心是动态控制元素的显示/隐藏状态,并合理绑定鼠标或键盘事件,同时注意层级关系和事件冒泡问题。关键不在于写多少代码,而在于结构清晰、响应准确、体验自然。
HTML 结构要语义化、嵌套明确
多级菜单依赖清晰的父子关系,推荐用嵌套的 <ul><li></ul> 结构,每个子菜单作为父 <li> 的子元素:
<nav id="main-nav">
<ul>
<li><a href="#">首页</a></li>
<li class="has-submenu">
<a href="#">产品</a>
<ul class="submenu">
<li><a href="#">Web 应用</a></li>
<li class="has-submenu">
<a href="#">移动应用</a>
<ul class="submenu">
<li><a href="#">iOS 版</a></li>
<li><a href="#">Android 版</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
这样 DOM 层级天然支持递归操作,也方便 CSS 定位与过渡动画。
用事件委托统一处理展开/收起逻辑
避免给每个菜单项单独绑定事件,而是监听父容器(如 #main-nav),利用事件冒泡和 target 判断是否点击了带子菜单的链接:
立即学习“Java免费学习笔记(深入)”;
- 只对带有
has-submenu类的<li>内的<a></a>做响应 - 调用
toggleSubmenu()方法切换其紧邻的.submenu显示状态 - 点击其他区域时,收起所有已展开的子菜单(可配合
document.addEventListener('click')实现)
示例 JS:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
const nav = document.getElementById('main-nav');
nav.addEventListener('click', e => {
const link = e.target.closest('li.has-submenu > a');
if (link) {
e.preventDefault();
const submenu = link.nextElementSibling;
if (submenu && submenu.classList.contains('submenu')) {
submenu.classList.toggle('show');
}
}
});
// 点击外部收起所有
document.addEventListener('click', e => {
if (!e.target.closest('#main-nav')) {
document.querySelectorAll('.submenu.show').forEach(el => el.classList.remove('show'));
}
});
CSS 配合控制显示与过渡效果
JavaScript 只负责切换类名(如 show),样式由 CSS 控制,更解耦也更利于动画:
.submenu {
display: none;
opacity: 0;
transform: translateY(-5px);
transition: all 0.2s ease;
}
.submenu.show {
display: block;
opacity: 1;
transform: translateY(0);
}
注意:不要用 visibility: hidden 或 height: 0 配合 overflow: hidden 做隐藏,因为多级菜单高度不确定;display: none + opacity 是更稳妥的选择。
增强可用性:支持键盘导航与焦点管理
为满足无障碍要求,需支持 Tab 键进入、Enter/Space 展开、Esc 收起:
- 给所有可展开的
<a></a>添加role="button"和aria-expanded="false" - 展开时更新
aria-expanded="true",并把焦点移到第一个子菜单项 - 监听
keydown:按Enter或Space触发展开,按Escape向上收起当前层级
例如:
link.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleSubmenu(link);
} else if (e.key === 'Escape') {
closeAllSiblings(link);
}
});
不复杂但容易忽略细节,重点是结构合理、事件精准、样式可控、体验完整。

















