无jQuery实现响应式多级下拉菜单 适配悬停/点击双触发
多级导航栏移动端点击失效修复
问题原因
- 原有JS仅给最外层一级菜单绑定了点击事件,二级、三级等嵌套下拉菜单完全没有绑定事件,点击深层节点无响应
- 自定义的
.show类仅设置了display: flex,但子菜单默认样式带有visibility: hidden、opacity: 0属性,即使添加show类也无法正常显示 - 没有做事件冒泡阻止,点击内层菜单会同时触发外层父菜单的点击事件,导致菜单状态异常
- 没有做屏幕宽度判断,桌面端也会触发点击展开逻辑,和已实现的悬停效果冲突
- 没有过滤纯跳转类菜单项,点击带
.link类的链接时也会触发展开逻辑,交互混乱
修复代码
1. CSS调整
删除原有全局定义的.show、.hide样式,在移动端媒体查询内补充show类规则,避免干扰桌面端悬停效果:
@media screen and (max-width: 1199px) { .menu ul li > ul.show { visibility: visible; opacity: 1; display: block; transition-delay: 0ms; } /* 可按需调整移动端子菜单位置,默认200%偏右可改成100% */ .menu ul li ul { left: 100%; } }
2. JS逻辑重写
替换原有JS代码,实现全层级菜单绑定、事件冒泡阻止、屏幕宽度判断、同级别菜单互斥逻辑:
function initMobileMenu() { // 宽度≥1200px为桌面端,走悬停逻辑,不绑定点击事件 if (window.innerWidth >= 1200) return; // 选中所有带子菜单的li节点(覆盖所有嵌套层级) const subMenuParents = document.querySelectorAll('.menu li:has(> ul)'); subMenuParents.forEach(li => { li.removeEventListener('click', toggleSubMenu); li.addEventListener('click', toggleSubMenu); }); } function toggleSubMenu(e) { // 阻止事件冒泡,避免点击子菜单触发父节点逻辑 e.stopPropagation(); // 点击纯跳转链接时不触发展开 if (e.target.closest('.link')) return; const currentSub = this.querySelector(':scope > ul'); if (!currentSub) return; // 关闭同级其他已展开的菜单 const siblingNodes = Array.from(this.parentElement.children).filter(node => node !== this); siblingNodes.forEach(node => { const openedSub = node.querySelector(':scope > ul.show'); if (openedSub) openedSub.classList.remove('show'); }); // 切换当前子菜单显示状态 currentSub.classList.toggle('show'); } // 页面加载完成初始化 document.addEventListener('DOMContentLoaded', initMobileMenu); // 窗口尺寸变化(如横竖屏切换)时重新初始化 window.addEventListener('resize', initMobileMenu);
说明
原有HTML结构不需要修改,替换对应CSS和JS代码后即可实现:桌面端悬停展开子菜单、移动端点击展开子菜单的效果,支持无限层级嵌套,同级别菜单同时只会展开一个。
内容的提问来源于stack exchange,提问作者Jovan Jovanovic
相关产品推荐
相关产品推荐

