JavaScript拖拽问题:多次点击图片后拖拽出现事件累加如何解决
问题根因
事件累加问题核心为2个错误:
- 事件监听函数引用不匹配:
mousedownMyPic中给drop区域绑定dragover、drop时,每次都在循环内声明新的临时函数,mouseupMyPic中移除时又重新声明了新的临时函数,二者内存地址完全不同,移除操作不生效,多次点击就会叠加绑定。 - 无重复绑定校验:每次触发mousedown都直接新增监听,没有判断当前是否已经绑定过拖拽相关事件,就算引用匹配也会出现重复绑定。
修复方案
提供2种可选修复方式,推荐第一种:
方案1:全局一次性绑定事件(更稳定,无需动态绑定卸载)
直接在初始化阶段完成所有事件绑定,通过状态变量控制拖拽逻辑是否生效,完全避免动态绑定带来的引用问题:
let myPictures = document.querySelectorAll('.myPictures'); let dropAreas = document.querySelectorAll('.dropAreaDrag'); // 新增状态变量,标记当前是否处于可拖拽触发状态 let isDragActive = false; let currentDraggedPic = null; // 一次性绑定图片的mousedown、mouseup、dragstart、dragend myPictures.forEach(function(myPic) { myPic.addEventListener('mousedown', evLiMyPic); function evLiMyPic(e){ if(!resize){ isDragActive = true; currentDraggedPic = myPic; } } myPic.addEventListener('mouseup', evLiMyPicMouseUp); function evLiMyPicMouseUp(e){ if(!isResizing){ isDragActive = false; currentDraggedPic = null; } } myPic.addEventListener('dragstart', dragstartMyPic); myPic.addEventListener('dragend', dragendMyPic); }); // 一次性绑定drop区域的dragover、drop dropAreas.forEach(function(dropArea) { dropArea.addEventListener('dragover', evLidragoverMyPic); function evLidragoverMyPic(e){ // 只有拖拽激活状态才执行逻辑 if(isDragActive) dragoverMyPic(e, dropArea); } dropArea.addEventListener('drop', evLidropMyPic); function evLidropMyPic(e){ if(isDragActive) { dropMyPic(e, dropArea); // 放置完成后重置状态 isDragActive = false; currentDraggedPic = null; } } }); function dragstartMyPic(e){ if(!isDragActive) return; // 原有逻辑 } function dragendMyPic(e){ // 拖拽结束重置状态 isDragActive = false; currentDraggedPic = null; // 原有逻辑 } function dragoverMyPic(e, dropArea){ // 原有逻辑 } function dropMyPic(e, dropArea){ // 原有逻辑 }
方案2:修复动态绑定的函数引用问题
如果确实需要动态绑定卸载事件,要确保绑定和移除用的是同一个函数引用,同时加标志位避免重复绑定:
let myPictures = document.querySelectorAll('.myPictures'); // 存储已绑定的事件引用,用于后续移除 let boundDropEvents = new Map(); myPictures.forEach(function(myPic) { // 新增标志位,避免重复绑定 myPic.isDragEventBound = false; myPic.addEventListener('mousedown', evLiMyPic); function evLiMyPic(e){ if(!resize && !myPic.isDragEventBound){ mousedownMyPic(e, myPic); myPic.isDragEventBound = true; } } myPic.addEventListener('mouseup', evLiMyPicMouseUp); function evLiMyPicMouseUp(e){ if(!isResizing && myPic.isDragEventBound){ mouseupMyPic(e, myPic); myPic.isDragEventBound = false; } } }); function mousedownMyPic(e, myPic){ myPic.addEventListener('dragstart', dragstartMyPic); myPic.addEventListener('dragend', dragendMyPic); let dropAreas = document.querySelectorAll('.dropAreaDrag'); // 存储当前绑定的事件引用 let currentEvents = []; dropAreas.forEach(function(dropArea) { const dragoverHandler = (e) => dragoverMyPic(e, dropArea); const dropHandler = (e) => dropMyPic(e, dropArea); dropArea.addEventListener('dragover', dragoverHandler); dropArea.addEventListener('drop', dropHandler); currentEvents.push({dropArea, dragoverHandler, dropHandler}); }); boundDropEvents.set(myPic, currentEvents); } function mouseupMyPic(e, myPic){ myPic.removeEventListener('dragstart', dragstartMyPic); myPic.removeEventListener('dragend', dragendMyPic); // 取出之前存储的事件引用执行移除 let currentEvents = boundDropEvents.get(myPic); if(currentEvents){ currentEvents.forEach(({dropArea, dragoverHandler, dropHandler}) => { dropArea.removeEventListener('dragover', dragoverHandler); dropArea.removeEventListener('drop', dropHandler); }); boundDropEvents.delete(myPic); } } // 原有事件处理函数保持不变 function dragstartMyPic(e){ // do something } function dragendMyPic(e){ // do something } function dragoverMyPic(e){ // do something } function dropMyPic(e){ // do something }
内容的提问来源于stack exchange,提问作者Harald
相关产品推荐
相关产品推荐

