removeEventListener失效求助:复用函数导致无法移除事件监听
事件移除失效的解决方案
嗨,这个问题我太熟悉啦!核心原因很简单:每次调用showContent()或hideContent()都会返回一个全新的匿名函数,你添加事件时用的函数引用,和移除时调用生成的函数引用根本不是同一个,removeEventListener自然找不到要移除的目标。
为什么会这样?
函数在JS里是引用类型,哪怕两个函数逻辑完全一样,只要是不同的实例,引用地址就不同。举个例子:
// 这两个函数逻辑相同,但引用完全不同 const func1 = showContent(loginSection); const func2 = showContent(loginSection); console.log(func1 === func2); // false
所以你添加事件时用的是func1,移除时却告诉浏览器要移除func2,浏览器当然找不到啦!
解决方法:缓存事件处理函数
我们只需要提前把每个元素对应的show和hide函数实例缓存起来,让addEventListener和removeEventListener用同一个引用就行。
方案1:手动缓存(适合少量元素)
const loginSection = document.querySelector('#js-login-section'); const searchSection = document.querySelector('#js-search-section'); const shoppingBagSection = document.querySelector('#js-shopping-bag-section'); const wishlistSection = document.querySelector('#js-wishlist-section'); function showContent(element) { return () => { const toggle = element.lastElementChild; toggle.style.maxHeight = toggle.scrollHeight + 'px'; } } function hideContent(element) { return () => { const toggle = element.lastElementChild; toggle.style.maxHeight = null; } } // 关键:提前生成并缓存每个元素的事件处理函数 const loginHandlers = { show: showContent(loginSection), hide: hideContent(loginSection) }; const searchHandlers = { show: showContent(searchSection), hide: hideContent(searchSection) }; const bagHandlers = { show: showContent(shoppingBagSection), hide: hideContent(shoppingBagSection) }; const wishlistHandlers = { show: showContent(wishlistSection), hide: hideContent(wishlistSection) }; // 添加事件:用缓存的函数引用 loginSection.addEventListener('mouseover', loginHandlers.show); loginSection.addEventListener('mouseout', loginHandlers.hide); searchSection.addEventListener('mouseover', searchHandlers.show); searchSection.addEventListener('mouseout', searchHandlers.hide); shoppingBagSection.addEventListener('mouseover', bagHandlers.show); shoppingBagSection.addEventListener('mouseout', bagHandlers.hide); wishlistSection.addEventListener('mouseover', wishlistHandlers.show); wishlistSection.addEventListener('mouseout', wishlistHandlers.hide); // 移除事件:用同一个缓存的引用 loginSection.removeEventListener('mouseover', loginHandlers.show); loginSection.removeEventListener('mouseout', loginHandlers.hide); searchSection.removeEventListener('mouseover', searchHandlers.show); searchSection.removeEventListener('mouseout', searchHandlers.hide); shoppingBagSection.removeEventListener('mouseover', bagHandlers.show); shoppingBagSection.removeEventListener('mouseout', bagHandlers.hide); wishlistSection.removeEventListener('mouseover', wishlistHandlers.show); wishlistSection.removeEventListener('mouseout', wishlistHandlers.hide);
方案2:用循环批量处理(更符合DRY原则)
如果元素数量多,用循环可以大幅减少重复代码:
const sectionSelectors = [ '#js-login-section', '#js-search-section', '#js-shopping-bag-section', '#js-wishlist-section' ]; function showContent(element) { return () => { const toggle = element.lastElementChild; toggle.style.maxHeight = toggle.scrollHeight + 'px'; } } function hideContent(element) { return () => { const toggle = element.lastElementChild; toggle.style.maxHeight = null; } } // 批量获取元素并缓存处理函数 const sectionData = sectionSelectors.map(selector => { const element = document.querySelector(selector); return { element, showHandler: showContent(element), hideHandler: hideContent(element) }; }); // 批量添加事件 sectionData.forEach(({ element, showHandler, hideHandler }) => { element.addEventListener('mouseover', showHandler); element.addEventListener('mouseout', hideHandler); }); // 批量移除事件 sectionData.forEach(({ element, showHandler, hideHandler }) => { element.removeEventListener('mouseover', showHandler); element.removeEventListener('mouseout', hideHandler); });
这样不管你后续要切换媒体查询的事件类型(比如移动端换成点击事件),都能准确移除旧事件啦!
内容的提问来源于stack exchange,提问作者mdblzk
相关产品推荐
相关产品推荐

