点击事件函数重复触发问题:为何模态框关闭函数执行次数递增?
模态框关闭函数重复执行的成因及解决方法
问题描述
我有一组带按钮的卡片网格,每个按钮绑定了打开模态框的click事件。模态框关闭后会执行相关逻辑,但出现异常:第二次打开并关闭模态框时,关闭函数执行2次;第三次操作时执行3次,以此类推。
代码示例
HTML代码
<div class="grid"> <div class="card"><button class="card__detail">Add</button></div> <div class="card"><button class="card__detail">Add</button></div> <div class="card"><button class="card__detail">Add</button></div> <div class="card"><button class="card__detail">Add</button></div> </div> <div class="modal"> <button class="modal__add">Add to basket</button> <div class="modal__hide"></div> </div>
JavaScript代码
const detailBtns = document.getElementsByClassName('card__detail'); const modal = document.querySelector('.modal'); for(let i = 0; i < detailBtns.length; i++) { const parent = detailBtns[i].parentNode; detailBtns[i].addEventListener("click", function() { windowLaunch(); }); } function windowLaunch() { modal.classList.add('open'); const hideModalBtn = modal.getElementsByClassName('modal__hide'); hideModalBtn[0].addEventListener("click", function() { modal.classList.remove('open'); }); }
问题成因
核心问题是重复绑定事件监听器:每次点击卡片按钮调用windowLaunch()打开模态框时,都会给modal__hide元素新增一个click事件监听器。第一次打开时绑定1个,第二次打开又叠加绑定1个,此时关闭按钮上就有2个监听器,点击关闭时自然会执行2次;第三次打开再新增1个,执行次数就变成3次,以此类推,执行次数随打开次数递增。
解决方法
方法1:将关闭事件移到外部,只绑定一次
把关闭按钮的事件监听器放在windowLaunch()外面,页面加载时只绑定一次,避免重复绑定:
const detailBtns = document.getElementsByClassName('card__detail'); const modal = document.querySelector('.modal'); // 提前获取关闭按钮,只绑定一次关闭事件 const hideModalBtn = modal.querySelector('.modal__hide'); hideModalBtn.addEventListener("click", function() { modal.classList.remove('open'); }); for(let i = 0; i < detailBtns.length; i++) { detailBtns[i].addEventListener("click", function() { windowLaunch(); }); } function windowLaunch() { modal.classList.add('open'); }
方法2:绑定前移除已有监听器(需用具名函数)
如果必须在windowLaunch()内绑定,可先移除之前的监听器再重新绑定,注意要使用具名函数(匿名函数无法被移除):
const detailBtns = document.getElementsByClassName('card__detail'); const modal = document.querySelector('.modal'); for(let i = 0; i < detailBtns.length; i++) { detailBtns[i].addEventListener("click", function() { windowLaunch(); }); } // 定义具名的关闭处理函数 function hideModalHandler() { modal.classList.remove('open'); } function windowLaunch() { modal.classList.add('open'); const hideModalBtn = modal.querySelector('.modal__hide'); // 先移除已有的监听器,再绑定新的 hideModalBtn.removeEventListener("click", hideModalHandler); hideModalBtn.addEventListener("click", hideModalHandler); }
方法3:使用事件委托(更优雅)
利用事件冒泡特性,直接给模态框绑定一次事件,通过判断点击目标来执行关闭逻辑,无需每次打开模态框时处理:
const detailBtns = document.getElementsByClassName('card__detail'); const modal = document.querySelector('.modal'); for(let i = 0; i < detailBtns.length; i++) { detailBtns[i].addEventListener("click", function() { modal.classList.add('open'); }); } // 事件委托:给模态框绑定一次点击事件 modal.addEventListener("click", function(e) { // 判断点击的是关闭按钮 if(e.target.classList.contains('modal__hide')) { modal.classList.remove('open'); } });
内容的提问来源于stack exchange,提问作者Mikita Tarasau
相关产品推荐
相关产品推荐

