在网页导航菜单的开发场景中,用户点击子菜单项后,往往希望对应的父级菜单能够保持展开状态,避免菜单自动收起影响后续操作。实现这个效果的核心逻辑是,当子菜单项被点击时,向上遍历其所有的父级UL元素,为这些UL元素添加show类,从而控制菜单的展开样式。

实现思路拆解
整个功能的实现可以分为三个核心步骤:
- 为所有子菜单项绑定点击事件,监听用户的点击操作
- 在点击事件触发后,从当前点击的子菜单项出发,向上查找所有的父级UL元素
- 为找到的每个父级UL元素添加show类,同时可以根据需要处理其他相关样式或状态
基础HTML结构示例
首先我们需要一个多级的导航菜单结构,作为功能实现的基础,结构如下:
<nav class="nav-menu">
<ul>
<li>
<span class="menu-parent">菜单1</span>
<ul class="sub-menu">
<li>
<span class="menu-parent">子菜单1-1</span>
<ul class="sub-menu">
<li><a href="#" class="menu-item">子菜单1-1-1</a></li>
<li><a href="#" class="menu-item">子菜单1-1-2</a></li>
</ul>
</li>
<li><a href="#" class="menu-item">子菜单1-2</a></li>
</ul>
</li>
<li>
<span class="menu-parent">菜单2</span>
<ul class="sub-menu">
<li><a href="#" class="menu-item">子菜单2-1</a></li>
<li><a href="#" class="menu-item">子菜单2-2</a></li>
</ul>
</li>
</ul>
</nav>
基础CSS样式
我们需要为sub-menu设置默认隐藏样式,同时定义show类对应的展开样式:
.sub-menu {
display: none;
padding-left: 20px;
}
.sub-menu.show {
display: block;
}
.menu-parent {
cursor: pointer;
font-weight: bold;
padding: 8px 0;
display: block;
}
.menu-item {
padding: 6px 0;
display: block;
color: #333;
text-decoration: none;
}
.menu-item:hover {
color: #1890ff;
}
JavaScript实现代码
接下来编写核心的JavaScript逻辑,实现点击子菜单项时为父级UL添加show类的功能:
// 获取所有子菜单项元素
const menuItems = document.querySelectorAll('.menu-item');
// 遍历所有子菜单项,绑定点击事件
menuItems.forEach(item => {
item.addEventListener('click', function(e) {
// 阻止默认跳转行为,方便测试
e.preventDefault();
// 获取当前点击元素的父级UL,也就是当前子菜单项所在的UL
let parentUl = this.closest('li').parentElement;
// 向上遍历所有父级UL元素
while (parentUl && parentUl.classList.contains('sub-menu')) {
// 为当前父级UL添加show类
parentUl.classList.add('show');
// 继续向上查找父级UL
parentUl = parentUl.parentElement.closest('.sub-menu');
}
// 可选:如果需要同时展开所有父级的父级菜单,可以添加以下逻辑
// 查找最外层的父级UL并添加show类
let topParentUl = this.closest('.nav-menu').querySelector('.sub-menu');
if (topParentUl) {
topParentUl.classList.add('show');
}
});
});
// 可选:为父级菜单标题添加点击展开收起功能
const menuParents = document.querySelectorAll('.menu-parent');
menuParents.forEach(parent => {
parent.addEventListener('click', function() {
const subMenu = this.nextElementSibling;
if (subMenu && subMenu.classList.contains('sub-menu')) {
subMenu.classList.toggle('show');
}
});
});
代码逻辑说明
上述代码的核心逻辑如下:
- 首先通过
querySelectorAll获取所有带有menu-item类的子菜单项,这些是用户点击的目标元素 - 为每个子菜单项绑定点击事件,当点击触发时,先通过
closest方法找到当前子菜单项所在的li元素,再获取其直接父级UL - 使用while循环向上遍历所有的父级UL元素,只要元素存在且带有sub-menu类,就为其添加show类,直到没有更多父级UL为止
- 额外添加了父级菜单标题的点击逻辑,方便用户手动展开收起菜单,提升交互体验
注意事项
在实际使用过程中,需要注意以下几点:
- 如果导航菜单的结构发生变化,比如父级UL的类名不是sub-menu,需要对应调整代码中的类名判断逻辑
- 如果页面中有多个独立的导航菜单,需要确保选择器的范围正确,避免影响到其他不相关的菜单结构
- 如果需要在页面加载时就根据当前路由展开对应的菜单,可以在页面加载完成后,先获取当前路由对应的子菜单项,模拟一次点击或者直接为其父级UL添加show类
适配路由场景的扩展
如果是在单页应用中,需要根据当前路由自动展开对应的菜单,可以参考以下扩展代码:
// 假设当前路由是 /sub1/sub1-1/sub1-1-1
const currentPath = window.location.pathname;
// 根据路由匹配对应的子菜单项,这里简化为匹配href包含路由的元素
const activeItem = document.querySelector(`.menu-item[href*="${currentPath}"]`);
if (activeItem) {
// 模拟点击,触发父级UL添加show类的逻辑
activeItem.click();
}
JavaScript导航菜单show类父级UL修改时间:2026-06-14 15:36:20