循环内引用外部变量的函数语义问题及导航栏子菜单移动端失效求助
问题2:移动端导航栏子菜单失效的原因与修复
原代码的核心问题
- 事件绑定时机单一:
mostrarHijos仅在执行时根据当前窗口宽度绑定事件,页面加载后切换窗口宽度(如从桌面端缩小到移动端),不会重新绑定对应事件,导致移动端无click事件监听。 - 重复DOM查询与索引风险:每次循环都调用
document.querySelectorAll,性能低下,且若DOM结构动态变化,索引[i]可能无法正确关联.sub-menu和.children元素。 - 未处理窗口resize事件:设备旋转或窗口缩放时,无法自动切换事件绑定逻辑,适配失效。
- 移动端click事件潜在问题:部分移动端浏览器对click事件有300ms延迟,若父元素存在触摸事件阻止冒泡,也会导致click事件无法触发。
修复后的代码实现
// 缓存DOM元素,避免重复查询 const subMenus = document.querySelectorAll(".sub-menu"); const hijosList = document.querySelectorAll(".children"); const menuRes = document.querySelector(".menu-res"); // 移除所有子菜单事件监听 function removeSubMenuEvents() { subMenus.forEach(subMenuItem => { subMenuItem.removeEventListener("click", handleMobileClick); subMenuItem.removeEventListener("touchstart", handleMobileClick); subMenuItem.removeEventListener("mouseover", handleDesktopOver); subMenuItem.removeEventListener("mouseleave", handleDesktopLeave); }); } // 移动端点击/触摸事件处理 function handleMobileClick(e) { e.preventDefault(); // 通过DOM结构关联子菜单,比索引更可靠 const hijos = this.nextElementSibling; if (hijos?.classList) { hijos.classList.toggle("children-expand"); hijos.classList.toggle("children-retread"); } } // 桌面端鼠标移入事件处理 function handleDesktopOver() { const hijos = this.nextElementSibling; if (hijos?.classList) { hijos.classList.add("children-expand"); hijos.classList.remove("children-retread"); } } // 桌面端鼠标移出事件处理 function handleDesktopLeave() { const hijos = this.nextElementSibling; if (hijos?.classList) { hijos.classList.remove("children-expand"); hijos.classList.add("children-retread"); } } // 根据窗口宽度配置事件 function setupSubMenuEvents() { removeSubMenuEvents(); const isMobile = window.innerWidth < 992; subMenus.forEach(subMenuItem => { if (isMobile) { subMenuItem.addEventListener("click", handleMobileClick); // 添加touchstart优化移动端响应速度 subMenuItem.addEventListener("touchstart", handleMobileClick, { passive: true }); } else { subMenuItem.addEventListener("mouseover", handleDesktopOver); subMenuItem.addEventListener("mouseleave", handleDesktopLeave); } }); // 桌面端隐藏移动端菜单 if (!isMobile) { menuRes.classList.remove("expand"); menuRes.classList.remove("hide"); } } // 初始化执行 setupSubMenuEvents(); // 监听窗口大小变化,动态更新事件 window.addEventListener("resize", setupSubMenuEvents);
关键优化点
- 缓存DOM元素:一次性查询所有需要的元素,减少DOM操作的性能开销。
- 命名函数替代匿名函数:方便移除事件监听,避免重复绑定导致的多次触发问题。
- DOM结构关联子菜单:用
this.nextElementSibling替代索引关联,避免DOM结构变化导致的元素不匹配。 - 监听resize事件:窗口宽度变化时自动重新配置事件,适配设备旋转或窗口缩放场景。
- 添加touchstart事件:优化移动端点击响应速度,解决300ms延迟问题。
内容的提问来源于stack exchange,提问作者Samuel Villalba
相关产品推荐
相关产品推荐

