HTML5拖拽中dragleave事件触发子元素致容器样式异常问题
解决HTML5拖拽时子元素导致容器样式丢失的问题
这个坑我之前踩过!问题的核心在于HTML5拖拽事件的触发逻辑:当你把拖拽元素从父容器移到它的子元素上时,浏览器会认为你离开了父容器,从而触发父容器的dragleave事件,但实际上你的鼠标还在父容器的范围内,这就导致样式被错误地移除了。
解决方案1:通过relatedTarget判断是否真的离开容器
我们可以在dragleave事件中,检查鼠标的新目标(event.relatedTarget)是不是父容器的后代元素——如果是,说明只是移到了子元素上,不需要移除样式;只有当新目标完全脱离父容器时,才恢复原样式。
首先添加一个辅助函数来判断元素关系:
function isDescendant(parent, child) { let node = child.parentNode; while (node != null) { if (node === parent) { return true; } node = node.parentNode; } return false; }
然后修改拖拽事件处理函数:
function dragStart(event) { event.dataTransfer.setData("Text", event.target.id); } function dragEnter(event) { // 使用currentTarget确保操作的是绑定事件的父容器,而非子元素 event.currentTarget.style.border = "3px dotted red"; } function dragLeave(event) { const parentContainer = event.currentTarget; // 检查是否真的离开了父容器范围 if (!isDescendant(parentContainer, event.relatedTarget) && event.relatedTarget !== parentContainer) { parentContainer.style.border = ""; } } function allowDrop(event) { event.preventDefault(); } function drop(event) { event.preventDefault(); const data = event.dataTransfer.getData("Text"); event.currentTarget.appendChild(document.getElementById(data)); // 放下后恢复容器样式 event.currentTarget.style.border = ""; }
解决方案2:使用计数器跟踪拖拽进入次数
另一种思路是用计数器记录当前进入容器的层级:每次触发dragenter计数器加1,触发dragleave计数器减1,只有当计数器归0时,才说明完全离开了容器,再移除样式。
需要注意的是,要给子元素也绑定相同的拖拽事件(因为dragenter/dragleave不冒泡):
<div class="droptarget" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" ondrop="drop(event)" ondragover="allowDrop(event)"> <div class="inner-droptarget" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)"></div> </div>
对应的JS代码:
let dragCount = 0; function dragStart(event) { event.dataTransfer.setData("Text", event.target.id); } function dragEnter(event) { const parentContainer = event.currentTarget.closest('.droptarget'); if (dragCount === 0) { parentContainer.style.border = "3px dotted red"; } dragCount++; } function dragLeave(event) { const parentContainer = event.currentTarget.closest('.droptarget'); dragCount--; if (dragCount === 0) { parentContainer.style.border = ""; } } // allowDrop和drop函数同方案1
完整可运行代码(方案1)
这里给出整合后的完整代码,直接运行就能看到效果:
<html> <head> <style> .droptarget { width: 100px; height: 100px; margin: 15px; margin-right: 100px; padding: 10px; border: 1px solid #aaaaaa; } .inner-droptarget { margin-left: 20px; margin-top: 20px; width: 50px; height: 50px; border: 1px solid green } </style> </head> <body> <p ondragstart="dragStart(event)" draggable="true" id="dragtarget">Drag me!</p> <div class="droptarget" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" ondrop="drop(event)" ondragover="allowDrop(event)"> <div class="inner-droptarget"></div> </div> <script> function isDescendant(parent, child) { let node = child.parentNode; while (node != null) { if (node === parent) { return true; } node = node.parentNode; } return false; } function dragStart(event) { event.dataTransfer.setData("Text", event.target.id); } function dragEnter(event) { event.currentTarget.style.border = "3px dotted red"; } function dragLeave(event) { const parentContainer = event.currentTarget; if (!isDescendant(parentContainer, event.relatedTarget) && event.relatedTarget !== parentContainer) { parentContainer.style.border = ""; } } function allowDrop(event) { event.preventDefault(); } function drop(event) { event.preventDefault(); const data = event.dataTransfer.getData("Text"); event.currentTarget.appendChild(document.getElementById(data)); event.currentTarget.style.border = ""; } </script> <p> Now the border of the outside div stays red even when dragging into the green div! </p> </body> </html>
这样修改后,不管你把拖拽元素移到父容器的哪个位置(包括子元素上),父容器的红色边框都会保持,直到你完全拖出父容器为止。
内容的提问来源于stack exchange,提问作者Shelef
相关产品推荐
相关产品推荐

