addEventListener多次触发:父函数执行重复绑定事件监听器如何解决?
问题原因
你遇到的addEventListener多次触发问题,核心原因是每次调用deleteMovieModal函数时,都会给确认、取消按钮新增一次点击事件监听器。且bind()方法每次执行都会返回全新的函数实例,你既没有存储这些绑定后函数的引用,也没有在新增绑定前移除旧的监听器,最终导致点击一次按钮会同时触发多个绑定的回调函数。
解决方案
以下是三种可直接落地的修复方案:
方案1:事件绑定提至外层,仅执行一次
把确认、取消的事件绑定逻辑从deleteMovieModal中挪到外层作用域,全局只绑定一次,需要删除的目标元素通过modal的自定义属性传递即可:
const cancelDeletion = () => { toggleBackdrop(); deleteModalConfirmation.classList.remove("visible"); // 操作完成后清空存储的待删除元素标识 delete deleteModalConfirmation.dataset.targetElementId; }; const confirmDeletion = () => { toggleBackdrop(); // 从dataset中取出待删除元素并操作 const targetElement = document.getElementById(deleteModalConfirmation.dataset.targetElementId); targetElement?.remove(); deleteModalConfirmation.classList.remove("visible"); delete deleteModalConfirmation.dataset.targetElementId; }; // 事件绑定仅执行一次,放在外层作用域 cancelDeletionElement.addEventListener("click", cancelDeletion); confirmDeletionElement.addEventListener("click", confirmDeletion); const deleteMovieModal = (element) => { toggleBackdrop(); deleteModalConfirmation.classList.add("visible"); // 把待删除元素的ID存入modal的自定义属性,也可直接根据你的场景存储元素引用 deleteModalConfirmation.dataset.targetElementId = element.id; };
如果目标元素没有固定ID,也可以用一个外层作用域的变量存储待删除元素,逻辑完全一致。
方案2:每次绑定前移除旧监听器
如果必须把绑定逻辑写在deleteMovieModal内部,需要提前在外层存储绑定后函数的引用,每次新增绑定前先移除旧的监听器:
// 外层作用域存储绑定后的函数引用 let boundCancelHandler = null; let boundConfirmHandler = null; const cancelDeletion = (element) => { toggleBackdrop(); deleteModalConfirmation.classList.remove("visible"); }; const confirmDeletion = (element) => { toggleBackdrop(); element.remove(); deleteModalConfirmation.classList.remove("visible"); }; const deleteMovieModal = (element) => { toggleBackdrop(); deleteModalConfirmation.classList.add("visible"); // 先移除之前绑定的旧监听器 if (boundCancelHandler) { cancelDeletionElement.removeEventListener("click", boundCancelHandler); } if (boundConfirmHandler) { confirmDeletionElement.removeEventListener("click", boundConfirmHandler); } // 存储新绑定的函数引用 boundCancelHandler = cancelDeletion.bind(this, element); boundConfirmHandler = confirmDeletion.bind(this, element); cancelDeletionElement.addEventListener("click", boundCancelHandler); confirmDeletionElement.addEventListener("click", boundConfirmHandler); };
方案3:使用事件委托
直接给modal的根元素绑定一次全局事件,通过事件冒泡判断点击的是确认还是取消按钮,无需反复绑定监听器:
// 仅给modal根元素绑定一次事件即可 deleteModalConfirmation.addEventListener("click", (e) => { if (e.target === cancelDeletionElement) { toggleBackdrop(); deleteModalConfirmation.classList.remove("visible"); delete deleteModalConfirmation.dataset.targetElementId; } else if (e.target === confirmDeletionElement) { toggleBackdrop(); const targetElement = document.getElementById(deleteModalConfirmation.dataset.targetElementId); targetElement?.remove(); deleteModalConfirmation.classList.remove("visible"); delete deleteModalConfirmation.dataset.targetElementId; } }) const deleteMovieModal = (element) => { toggleBackdrop(); deleteModalConfirmation.classList.add("visible"); deleteModalConfirmation.dataset.targetElementId = element.id; };
内容的提问来源于stack exchange,提问作者user17316898
相关产品推荐
相关产品推荐

