我正在尝试使用 HTML5 拖放,并使可放置容器在可拖动元素位于其上方时更改其样式。 问题是,如果可放置容器包含内部元素,则会触发 Dragleave 事件,从而使容器失去其样式。
正如您所看到的,当可拖动元素进入小绿色框时。我们失去了外部 div 的红色边框。
<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 dragStart(event) {
event.dataTransfer.setData("Text", event.target.id);
}
function dragEnter(event) {
if ( event.target.className == "droptarget" ) {
event.target.style.border = "3px dotted red";
}
}
function dragLeave(event) {
if ( event.target.className == "droptarget" ) {
event.target.style.border = "";
}
}
</script>
<p>
The border of the outside div should remain red even if dragging into the green div!
</p>
</body>
</html>
您必须忽略在
"dragleave"
中包含的元素上触发的 "droptarget"
事件(例如 "inner-droptarget"
)。
为此,您可以检测事件是否是从放置区域的后代触发的。您的处理程序将如下所示:
function dragLeave(event) {
if ( event.target.className == "droptarget" ) && !($('.droptarget').contains(event.fromElement) {
event.target.style.border = "";
}
}
你可以使用计数器来解决这个问题。当draenter counter++时,当dragleave counter--并检查counter === 0时,然后做一些事情。
// div has an child element
divEl.addEventListen("dragenter",dragEnter)
divEl.addEventListen("dragleave",dragLeave)
counter = 0
function dragEnter(){
counter++
}
function dragLeave(){
counter--
if(counter === 0){
// then do something
}
}
当您在拖动过程中进入内部 div 时,将触发事件“拖动进入”和“拖动离开”事件。我们需要一种方法来标记您要离开外部 div 并进入内部 div,以便外部 div 上的边框保持红色。
我可以让两个边框都变成红色,但是当你留下类似的问题时,两个边框都会变回绿色。
<body>
<p ondragstart="dragStart(event)" draggable="true" id="dragtarget">Drag me!</p>
<div id="myOuterDiv" class="droptarget" ondragenter="dragEnter1(event)"
ondragleave="dragLeave1(event)"
ondrop="drop(event)"
ondragover="allowDrop(event)">
<div id="myInnerDiv" class="inner-droptarget" ondragenter="dragEnter2(event)"
ondragleave="dragLeave2(event)">
</div>
</div>
<script>
var inOuter = false;
var inInner = false;
function dragStart(event) {
event.dataTransfer.setData("Text", event.target.id);
}
function dragEnter1(event) {
inOuter = true;
document.getElementById("myInnerDiv").addEventListener("ondragenter", dragEnter2);
console.log("entered outer");
return highlightBorder();
}
function dragLeave1(event) {
console.log("left outer");
inOuter = false;
highlightBorder();
event.preventDefault();
}
function dragEnter2(event) {
console.log("entered inner");
inInner = true;
inOuter = true;
return highlightBorder();
}
function dragLeave2(event) {
console.log("left inner");
inInner = false;
inOuter = true;
return highlightBorder();
}
function allowDrop(ev) {
return false;
}
function drop(event) {
var inOuter = false;
var inInner = false;
document.getElementById("myInnerDiv").style.border ="3px solid green";
document.getElementById("myOuterDiv").style.border ="3px solid green";
return false;
}
function highlightBorder() {
if( inInner)
{
document.getElementById("myInnerDiv").style.border ="3px dotted red";
document.getElementById("myOuterDiv").style.border ="3px dotted red";
return false;
}
if(!inInner && inOuter)
{
document.getElementById("myInnerDiv").style.border ="";
document.getElementById("myOuterDiv").style.border ="3px dotted red";
return false;
}
if(!inInner && !inOuter)
{
document.getElementById("myInnerDiv").style.border ="";
document.getElementById("myOuterDiv").style.border ="";
return false;
}
}
</script>
<p>
The border of the outside div should remain red even if dragging into the green div!
</p>
</body>
</html>
dragleave
事件的行为确实会在子元素上触发,这通常不是您想要的反应。因此,有多种技术可以检测 Dragleave 何时实际指示用户的拖动何时退出预期的放置区域。
pointer-events: none
的技术使用户无法单击拖放区内的按钮。
function dragleave_has_exited_current_target(event: DragEvent) {
const mouse_is_outside_viewport = !event.relatedTarget || (
event.clientX === 0 &&
event.clientY === 0
)
if (mouse_is_outside_viewport)
return true
const rect = (event.currentTarget as any).getBoundingClientRect()
const withinX = event.clientX >= rect.left && event.clientX <= rect.right
const withinY = event.clientY >= rect.top && event.clientY <= rect.bottom
const mouse_is_outside_current_target = !(withinX && withinY)
return mouse_is_outside_current_target
}
您可以在任何 Dragleave 侦听器中使用此技术,如下所示:
function dragleave(event) {
if (dragleave_has_exited_current_target(event)) {
// proceed with your own logic here
remove_dropzone_indicator()
}
}
请注意,在 javascript 中,event.currentTarget
是您附加事件侦听器本身的元素 - 这意味着,为了使此技术逐字工作,您必须将 Dragleave 侦听器直接附加到预期的 dropzone 元素上。