TypeScript开发中removeEventListener无法移除事件监听器问题求助
问题核心原因
- 事件监听绑定/移除时传入的回调不是同一个引用:
addEventListener中传入的是每次触发都会重新生成的匿名箭头函数,removeEventListener中又传入了另一个全新的匿名函数,二者内存地址不同,浏览器无法匹配到需要移除的监听事件,导致旧监听一直残留,出现重复绑定问题。 - 移除监听时的回调写法本身存在逻辑错误:
() => handleAudioPlayer仅返回了函数本身,没有调用handleAudioPlayer也没有传递要求的两个参数,即使引用一致也无法匹配原绑定逻辑。
修复方案
方案1:将监听回调挂载到元素属性,保证引用唯一
直接把回调存到audio元素的自定义属性上,绑定和移除都使用同一个引用,修正后代码如下:
if(audio.paused) { // 绑定前先移除可能残留的旧监听,避免极端场景重复绑定 if((audio as any)._audioTimeUpdateHandler) { audio.removeEventListener('timeupdate', (audio as any)._audioTimeUpdateHandler, true); delete (audio as any)._audioTimeUpdateHandler } audio.play() // 把回调函数存到audio的自定义属性上,保证后续移除时能拿到同一个引用 (audio as any)._audioTimeUpdateHandler = (e) => handleAudioPlayer( e.target as HTMLAudioElement, audio.parentElement as HTMLDivElement ) audio.addEventListener('timeupdate', (audio as any)._audioTimeUpdateHandler, true); } else { audio.pause() // 移除时直接取之前存的回调引用 if((audio as any)._audioTimeUpdateHandler) { audio.removeEventListener('timeupdate', (audio as any)._audioTimeUpdateHandler, true); delete (audio as any)._audioTimeUpdateHandler } }
如果需要规避TS类型报错,可以提前给HTMLAudioElement扩展自定义属性的类型声明,或直接使用as any临时处理类型校验。
方案2:使用AbortController(现代浏览器支持,写法更简洁)
利用AbortController的signal一次性取消所有关联监听,不需要手动维护函数引用:
// 控制器可以存在audio自定义属性或者外层作用域 let audioAbortController: AbortController | null = null if(audio.paused) { // 先终止之前的监听,避免重复绑定 audioAbortController?.abort() audio.play() audioAbortController = new AbortController() audio.addEventListener('timeupdate', (e) => handleAudioPlayer( e.target as HTMLAudioElement, audio.parentElement as HTMLDivElement ), { capture: true, signal: audioAbortController.signal }); } else { audio.pause() // 直接调用abort即可移除所有关联该signal的监听 audioAbortController?.abort() audioAbortController = null }
内容的提问来源于stack exchange,提问作者Lun
相关产品推荐
相关产品推荐

