Javascript resize事件下removeEventListener失效 多分辨率导航栏适配问题
问题解决方案
一、resize场景下removeEventListener失效问题
失效根因
- 事件引用不匹配:你注册事件时使用匿名函数,
removeEventListener要求传入的函数引用必须和addEventListener时完全一致,匿名函数每次执行都会生成新的引用,无法匹配到已注册的函数,导致移除失败。 - 移除目标错误:
menu_mouseOver、menu_onFocus等函数是给DOM元素绑定事件,并非绑定在window的resize事件上,你之前在onResize里调用window.removeEventListener('resize', menu_onClick)属于操作目标完全错误,自然无效。 - 未前置清理重复绑定:每次
resize触发你都会重新调用绑定函数,没有先清理上一次绑定的事件,导致同一个元素上堆叠了多个相同的事件监听。
修复方案
步骤1:新增防抖和统一清理逻辑
// resize防抖,避免频繁调整窗口时重复执行逻辑 function debounce(func, delay = 300) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); } } // 统一清理桌面端专属事件 function clearDesktopEvents() { overEntry.forEach(sub => { const subParent = sub.parentElement; if(subParent._mouseOverHandler) { subParent.removeEventListener('mouseover', subParent._mouseOverHandler); subParent.removeEventListener('mouseout', subParent._mouseOutHandler); delete subParent._mouseOverHandler; delete subParent._mouseOutHandler; } }); focusEntry.forEach(sub => { if(sub._keyupHandler) { sub.removeEventListener('keyup', sub._keyupHandler); delete sub._keyupHandler; } }); focusDetectionEntry.forEach(sub => { const subParent = sub.parentElement; if(sub._focusHandler) { sub.removeEventListener('focus', sub._focusHandler); subParent.removeEventListener('focusout', subParent._focusoutHandler); delete sub._focusHandler; delete subParent._focusoutHandler; } }); }
步骤2:修改事件绑定逻辑,将处理函数挂载到元素属性上
以menu_mouseOver为例,其余menu_onFocus、menu_onFocusDetection按照相同逻辑修改:
function menu_mouseOver() { overEntry.forEach(sub => { const subParent = sub.parentElement; // 避免重复绑定 if(subParent._mouseOverHandler) return; // 把处理函数挂载到元素自定义属性上,保证移除时引用一致 subParent._mouseOverHandler = function(e) { subParent.setAttribute('aria-expanded', 'true'); e.stopPropagation(); }; subParent._mouseOutHandler = function(e) { subParent.setAttribute('aria-expanded', 'false'); e.stopPropagation(); }; subParent.addEventListener('mouseover', subParent._mouseOverHandler); subParent.addEventListener('mouseout', subParent._mouseOutHandler); }) }
步骤3:修改初始化和resize逻辑
点击事件是全分辨率通用的,只需要在页面加载时绑定一次即可,不需要每次resize重复绑定:
function onLoadFunction(e) { menu_burger(); menu_onClick(); // 全分辨率通用,仅绑定一次 onResizeFunction(); // 给resize加防抖 window.addEventListener("resize", debounce(onResizeFunction)); } function onResizeFunction(e) { // 先清理之前绑定的桌面端事件 clearDesktopEvents(); if (window.innerWidth > 770) { // 大于770px时绑定桌面端专属交互 menu_mouseOver(); menu_onFocus(); menu_onFocusDetection(); } }
二、stopImmediatePropagation的替代方案
stopImmediatePropagation会同时阻止事件冒泡、以及当前元素上同事件类型的其他监听函数执行,你的场景完全不需要这么强的限制,反而会影响后续功能扩展,推荐两种更优方案:
- 直接替换为
stopPropagation():仅阻止事件向上冒泡,不会影响当前元素的其他监听函数,完全满足你的需求。 - 省略绑定/移除逻辑,直接加分辨率判断:所有桌面端专属的事件处理函数开头加分辨率校验,不符合条件直接返回即可,完全省去绑定、移除事件的复杂逻辑,示例:
subParent._mouseOverHandler = function(e) { // 小于等于770px时直接不执行逻辑 if(window.innerWidth <=770) return; subParent.setAttribute('aria-expanded', 'true'); e.stopPropagation(); };
这种方案下桌面端事件只需要在页面加载时绑定一次即可,不需要监听resize做绑定/移除操作,逻辑更简洁,也不会出现事件移除失败的问题。
内容的提问来源于stack exchange,提问作者TGO
相关产品推荐
相关产品推荐

