窗口缩小至960px以下时无法移除Event Listener的问题求助
窗口缩小至960px以下时无法移除Event Listener的问题求助
问题核心原因
我一眼就定位到问题所在了:你把dropopen和dropclose定义在了bindIt函数的内部!
每次触发load或者resize事件时,bindIt都会被重新执行一遍,这时候函数内部的dropopen和dropclose会被重新创建成全新的函数实例——也就是说,第一次窗口大于960px时添加的事件监听器,对应的是第一次bindIt执行时创建的那两个函数;而当你缩小窗口触发resize、再次执行bindIt时,你用新创建的dropopen/dropclose去调用removeEventListener,这两个新函数和之前添加的完全不是同一个引用,浏览器自然找不到要移除的事件监听器,所以移除操作直接失效了。
另外还有个隐藏问题:每次窗口大于960px时,bindIt都会重复添加事件监听器,导致鼠标事件触发时dropopen/dropclose会被执行多次,哪怕后续能成功移除,也得移除多次才能清干净。
修复方案
最直接的解决办法是把dropopen和dropclose移到bindIt函数外面,让它们成为固定的全局(或外层作用域)函数,这样每次add/remove操作的都是同一个函数引用:
// 把这两个函数移到bindIt外面,确保引用固定不变 function dropopen() { var attribute = this.getAttribute("group"); if (this.querySelector('#' + attribute) != null) { document.getElementById(attribute).style.display = 'block'; } } function dropclose() { var attribute = this.getAttribute("group"); if (this.querySelector('#' + attribute) != null) { document.getElementById(attribute).style.display = 'none'; } } window.addEventListener("load", function() { bindIt(); }); window.addEventListener("resize", function() { bindIt(); }); function bindIt() { var elements = document.getElementsByClassName("DropMenu"); // 优化:先统一移除事件监听器,避免重复添加 for (var i = 0; i < elements.length; i++) { elements[i].removeEventListener('mouseover', dropopen, false); elements[i].removeEventListener('mouseout', dropclose, false); } if (window.innerWidth > 960) { for (var i = 0; i < elements.length; i++) { elements[i].addEventListener('mouseover', dropopen, false); elements[i].addEventListener('mouseout', dropclose, false); } } }
额外优化建议
- 合并循环操作:像上面的代码那样,把移除和添加的循环合并,减少DOM操作次数,提升性能。
- 使用事件委托替代逐个绑定:如果你的下拉菜单结构固定,可以考虑用事件委托,只在父元素上绑定一次事件,根据事件源判断是否触发下拉逻辑,这样就不用每次resize都去操作多个元素的事件监听器了。
- 防抖处理resize事件:resize事件触发频率很高,建议加个防抖函数,避免
bindIt被频繁调用,比如:
function debounce(func, delay = 200) { let timer; return function() { clearTimeout(timer); timer = setTimeout(func, delay); } } // 用防抖后的函数绑定resize事件 window.addEventListener("resize", debounce(bindIt));
内容来源于stack exchange
相关产品推荐
相关产品推荐

