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

Javascript resize事件下removeEventListener失效 多分辨率导航栏适配问题

问题解决方案

一、resize场景下removeEventListener失效问题

失效根因

  1. 事件引用不匹配:你注册事件时使用匿名函数,removeEventListener要求传入的函数引用必须和addEventListener时完全一致,匿名函数每次执行都会生成新的引用,无法匹配到已注册的函数,导致移除失败。
  2. 移除目标错误:menu_mouseOver、menu_onFocus等函数是给DOM元素绑定事件,并非绑定在window的resize事件上,你之前在onResize里调用window.removeEventListener('resize', menu_onClick)属于操作目标完全错误,自然无效。
  3. 未前置清理重复绑定:每次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会同时阻止事件冒泡、以及当前元素上同事件类型的其他监听函数执行,你的场景完全不需要这么强的限制,反而会影响后续功能扩展,推荐两种更优方案:

  1. 直接替换为stopPropagation():仅阻止事件向上冒泡,不会影响当前元素的其他监听函数,完全满足你的需求。
  2. 省略绑定/移除逻辑,直接加分辨率判断:所有桌面端专属的事件处理函数开头加分辨率校验,不符合条件直接返回即可,完全省去绑定、移除事件的复杂逻辑,示例:
subParent._mouseOverHandler = function(e) {
  // 小于等于770px时直接不执行逻辑
  if(window.innerWidth <=770) return;
  subParent.setAttribute('aria-expanded', 'true');
  e.stopPropagation();
};

这种方案下桌面端事件只需要在页面加载时绑定一次即可,不需要监听resize做绑定/移除操作,逻辑更简洁,也不会出现事件移除失败的问题。

内容的提问来源于stack exchange,提问作者TGO

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 04:36:08