
本文详解如何在响应式全宽移动端导航中禁用页面滚动,并增强子菜单交互逻辑,使其支持点击父项两次(即 toggle)来展开/收拢,同时兼顾可访问性与用户体验。
本文详解如何在响应式全宽移动端导航中禁用页面滚动,并增强子菜单交互逻辑,使其支持点击父项两次(即 toggle)来展开/收拢,同时兼顾可访问性与用户体验。
在构建现代移动端友好导航时,两个常见但关键的交互问题常被忽略:一是全屏菜单展开时用户仍可滚动背景内容,造成视觉干扰与操作困惑;二是带下拉子菜单的父项仅支持单向展开(如 hover 或首次点击),缺乏二次点击收起能力,降低可用性。本文提供一套完整、健壮且符合无障碍标准(ARIA)的解决方案。
✅ 一、禁用背景滚动(overflow: hidden 动态控制)
当移动端菜单激活时,应阻止 <body> 的滚动行为,防止用户误操作导致页面偏移。核心逻辑是在 toggleMenu() 中动态设置 document.body.style.overflow:
function toggleMenu() {
const body = document.body;
const isExpanded = navMenu.classList.contains('active');
// 切换菜单状态
navMenu.classList.toggle('active');
// 同步更新 ARIA 属性
const updateAria = (el, value) => el && el.setAttribute('aria-expanded', value);
updateAria(hamburger, isExpanded);
updateAria(closeButton, isExpanded);
// 控制 body 滚动(关键修复)
body.style.overflow = isExpanded ? 'hidden' : 'auto';
// 同步关闭按钮显示
const closeIconWrapper = document.getElementById('mobile-close-icon-wrapper');
if (closeIconWrapper) {
closeIconWrapper.style.display = isExpanded ? 'block' : 'none';
}
}⚠️ 注意事项:
- 避免直接修改 document.documentElement.style.overflow(可能影响 iOS Safari 的 viewport 行为);优先使用 body。
- 若页面已存在 overflow: hidden 样式(如模态框叠加),建议用 CSS 类管理(如 .no-scroll { overflow: hidden; }),便于复用和避免样式冲突。
✅ 二、子菜单支持双击切换(Click-to-Toggle)
当前 CSS 中依赖 :hover 实现子菜单显示(.has-submenu:hover .submenu { display: block; }),这在触摸设备上不可靠且不支持“收起”。需彻底移除该规则,改用 JavaScript + CSS 类驱动:
1. 更新 CSS(移除 hover,启用 active 类)
/* 删除或注释掉这一行 */
/* .vassil-main-navigation .custom-nav-menu .has-submenu:hover .submenu { display: block; } */
/* 替换为基于 active 类的控制 */
.vassil-main-navigation .custom-nav-menu .has-submenu.active .submenu {
display: block;
}2. 添加子菜单交互逻辑(推荐委托式写法,支持多级)
// 使用事件委托,避免硬编码特定元素(如 Services)
document.addEventListener('click', function(e) {
const parentLi = e.target.closest('.has-submenu');
if (!parentLi || !e.target.closest('a')) return;
e.preventDefault(); // 阻止默认跳转(对 '#' 链接尤其重要)
// 切换 active 状态
parentLi.classList.toggle('active');
// 可选:确保其他子菜单关闭(单开模式)
const otherParents = document.querySelectorAll('.has-submenu:not(.active)');
otherParents.forEach(el => el.classList.remove('active'));
});✅ 优势:
- 支持任意数量的 .has-submenu 元素,无需为每个单独绑定事件;
- 自动兼容未来新增的子菜单项;
- e.preventDefault() 保证 href="#" 不触发页面跳顶。
✅ 三、增强健壮性与可访问性
- Esc 键关闭菜单:已在原代码中实现,保留即可;
- 焦点管理:菜单激活后,建议将焦点移至首个可聚焦元素(如第一个 <a>),关闭时恢复至触发按钮(需 hamburger.focus());
- 过渡动画兼容性:overflow: hidden 在 Safari 中可能引发布局抖动,可添加 body { will-change: scroll-position; } 优化渲染性能。
? 完整整合提示
将上述 JS 逻辑整合进原有 DOMContentLoaded 回调中,确保执行顺序正确(先 DOM 加载,再绑定事件)。最终效果:
? 点击汉堡图标 → 菜单展开 + 页面禁止滚动 + 关闭按钮显示;
? 点击“Services”父项 → 子菜单展开;再点一次 → 子菜单收起;
? Esc 键或点击关闭按钮 → 菜单收起 + 滚动恢复 + 关闭按钮隐藏。
此方案轻量、无依赖、语义清晰,适用于 WordPress 主题、静态站点及各类 CMS 前端定制场景。


















