移动端全屏菜单禁用滚动+点击父级<li>可二次收起子菜单
自定义导航优化方案
我制作了一个自定义导航,在小屏设备下会转为汉堡图标。点击图标会展开全屏菜单,但目前用户仍可滚动页面,需禁用该无效滚动;另外,其中一个链接下的子菜单点击父
<li>可展开,希望实现二次点击时收起子菜单,支持反复切换。
1. 禁用全屏菜单时的页面滚动
通过给<body>添加类来锁定滚动,同时记录当前滚动位置,避免菜单关闭后页面位置偏移:
- 菜单激活时,给body添加锁定类,固定页面位置
- 菜单关闭时,移除锁定类并恢复之前的滚动位置
2. 子菜单点击切换功能
给带有子菜单的父项绑定点击事件,切换子菜单的显示状态,同时阻止父链接的默认跳转行为:
- 移动端覆盖hover触发逻辑,改用点击事件控制子菜单
- 通过切换父
<li>的active类来控制子菜单的显示/隐藏
修改后的完整代码
HTML
<nav class="vassil-main-navigation"> <!-- Hamburger button --> <button class="hamburger-custom" id="hamburger-custom"> <span class="bar"></span> <span class="bar"></span> <span class="bar"></span> </button> <!-- Navigation Menu --> <ul class="custom-nav-menu" id="custom-nav-menu"> <li><a href="#"><span>Home</span></a></li> <li><a href="#"><span>About Us</span> </a></li> <li class="has-submenu"> <a href="#"><span>Services</span></a> <ul class="submenu"> <li><a href="#"><span>Kitchen Remodeling</span></a></li> <li><a href="#"><span>Bathroom Remodeling</span></a></li> <li><a href="#"><span>Complete Remodeling</span></a></li> </ul> </li> <li><a href="#"><span>Gallery</span></a></li> <li><a href="#"><span>Contact Us</span></a></li> </ul> <!-- 'X' button to close the fullscreen menu --> <div id="mobile-close-icon-wrapper"> <a href="javascript:void(0)" class="mobile-close-icon" id="mobile-close-icon">×</a> </div> </nav>
CSS
.vassil-main-navigation .custom-nav-menu { list-style-type: none; margin: 0; padding: 0; display: flex; justify-content: space-between; } .vassil-main-navigation .custom-nav-menu li { position: relative; } .vassil-main-navigation .custom-nav-menu a { text-decoration: none; color: inherit; display: flex; align-items: center; height: 100%; } .vassil-main-navigation .custom-nav-menu a span { display: inline-block; position: relative; } .vassil-main-navigation .custom-nav-menu a span::after { content: ''; position: absolute; width: 0; height: 1px; bottom: 0; left: 0; background-color: black; transition: width 0.4s; visibility: hidden; } .vassil-main-navigation .custom-nav-menu a:hover span::after { visibility: visible; width: 100%; } .vassil-main-navigation .custom-nav-menu .submenu { display: none; position: absolute; top: 100%; left: 0; padding: 0; list-style: none; background-color: #fff; z-index: 1; } .vassil-main-navigation .custom-nav-menu .has-submenu:hover .submenu { display: block; } .vassil-main-navigation .custom-nav-menu .submenu li a { padding: 10px 25px; white-space: nowrap; } .has-submenu > a:after { content: "▼"; font-size: 10px; margin-left: 5px; } #hamburger-custom { display: none; } /* 新增:滚动锁定样式 */ body.menu-locked { overflow: hidden; position: fixed; width: 100%; } /* Media query for smaller screens (max-width: 1024px) */ @media (max-width: 1024px) { #hamburger-custom { display: flex; flex-direction: column; justify-content: space-around; width: 30px; height: 25px; background: transparent; border: none; cursor: pointer; padding: 0; z-index: 10; } #hamburger-custom:focus { outline: none; } #hamburger-custom .bar { display: block; width: 30px; height: 3px; background: #333; border-radius: 10px; transition: 0.3s ease-in-out; } .vassil-main-navigation .custom-nav-menu { position: fixed; top: 0; left: -100%; width: 100%; height: 100vh; flex-direction: column; justify-content: center; list-style: none; background-color: rgba(255, 255, 255, 0.95); z-index: 1000; transition: left 0.3s ease; } .vassil-main-navigation .custom-nav-menu.active { left: 0; } .vassil-main-navigation .custom-nav-menu li a { text-align: center; padding: 15px; display: block; } .vassil-main-navigation .custom-nav-menu .has-submenu .submenu { display:none; position: absolute; top: 100%; left: 0; width: 100%; box-shadow: none; z-index: 1000; background-color: #fff; } #mobile-close-icon-wrapper { position: fixed; top: 0; right: 0; z-index: 1002; display: none; padding: 10px; } .custom-nav-menu.active + #mobile-close-icon-wrapper { display: block; } .mobile-close-icon { font-size: 30px; color: #333; text-decoration: none; } /* 移动端子菜单点击切换逻辑,覆盖hover效果 */ .vassil-main-navigation .custom-nav-menu .has-submenu:hover .submenu { display: none; } .vassil-main-navigation .custom-nav-menu .has-submenu.active .submenu { display: block; } } @media (min-width: 1025px) { #mobile-close-icon-wrapper { display: none !important;} }
JavaScript
document.addEventListener('DOMContentLoaded', function() { const hamburger = document.getElementById('hamburger-custom'); const navMenu = document.getElementById('custom-nav-menu'); const closeButton = document.getElementById('mobile-close-icon'); const body = document.body; let scrollY = 0; if (!hamburger || !navMenu || !closeButton) { console.error('Critical elements for the navigation menu are missing!'); return; } // 滚动锁定/解锁 function toggleScrollLock(isLocked) { if (isLocked) { scrollY = window.scrollY; body.classList.add('menu-locked'); body.style.top = `-${scrollY}px`; } else { body.classList.remove('menu-locked'); window.scrollTo(0, scrollY); body.style.top = ''; } } // 菜单切换 function toggleMenu() { const isExpanded = !navMenu.classList.contains('active'); navMenu.classList.toggle('active'); hamburger.setAttribute('aria-expanded', isExpanded); closeButton.setAttribute('aria-expanded', isExpanded); const closeIconWrapper = document.getElementById('mobile-close-icon-wrapper'); if (closeIconWrapper) { closeIconWrapper.style.display = isExpanded ? 'block' : 'none'; } toggleScrollLock(isExpanded); } // 子菜单切换 function toggleSubmenu(e) { const parentLi = e.currentTarget.parentElement; e.preventDefault(); parentLi.classList.toggle('active'); } // 绑定事件 hamburger.addEventListener('click', toggleMenu); closeButton.addEventListener('click', toggleMenu); const submenuLinks = document.querySelectorAll('.has-submenu > a'); submenuLinks.forEach(link => { link.addEventListener('click', toggleSubmenu); }); // ESC键关闭菜单 window.addEventListener('keydown', function(event) { if (event.key === 'Escape' && navMenu.classList.contains('active')) { toggleMenu(); } }); });
内容的提问来源于stack exchange,提问作者John Holliday
相关产品推荐
相关产品推荐

