You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移动端全屏菜单禁用滚动+点击父级<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">&times;</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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 15:52:03