我遇到的问题是,一个元素的dragleave事件是在悬停该元素的子元素时触发的。同样,当再次悬停回父元素时,dragenter不会被触发。

我做了一个简化的小提琴:http://jsfiddle.net/pimvdb/HU6Mk/1/。

HTML:

<div id="drag" draggable="true">drag me</div>

<hr>

<div id="drop">
    drop here
    <p>child</p>
    parent
</div>

使用以下JavaScript:

$('#drop').bind({
                 dragenter: function() {
                     $(this).addClass('red');
                 },

                 dragleave: function() {
                     $(this).removeClass('red');
                 }
                });

$('#drag').bind({
                 dragstart: function(e) {
                     e.allowedEffect = "copy";
                     e.setData("text/plain", "test");
                 }
                });

它应该做的是,当拖拽一些东西时,通过将drop div设置为红色来通知用户。这是可行的,但是如果你拖动到p子元素中,拖动键就会触发,div就不再是红色了。移动回下拉div也不会使它再次变红。有必要完全移出拖放div,并再次拖回它,使其变为红色。

是否有可能阻止dragleave在拖动到子元素时发射?

2017年更新:TL;DR,查找CSS指针事件:无;如@ h.d.所述。在现代浏览器和IE11中都能运行。


当前回答

问题是当鼠标移动到子元素前面时,dragleave事件被触发。

我已经尝试了各种方法来检查e.target元素是否与this元素相同,但没有任何改进。

我修复这个问题的方式有点hack,但工作100%。

dragleave: function(e) {
               // Get the location on screen of the element.
               var rect = this.getBoundingClientRect();

               // Check the mouseEvent coordinates are outside of the rectangle
               if(e.x > rect.left + rect.width || e.x < rect.left
               || e.y > rect.top + rect.height || e.y < rect.top) {
                   $(this).removeClass('red');
               }
           }

其他回答

当鼠标指针离开目标容器的拖动区域时触发"dragleave"事件。

这很有道理,因为在许多情况下,只有父母可以放弃,而不是后代。 我认为event. stoppropagation()应该处理这种情况,但似乎它没有做到这一点。

上面提到的一些解决方案似乎在大多数情况下都有效,但在那些不支持dragenter / dragleave事件的情况下失败了,比如iframe。

解决方法之一是检查事件。并验证它是否驻留在容器中,然后忽略dragleave事件,就像我在这里所做的那样:

function isAncestor(node, target) {
    if (node === target) return false;
    while(node.parentNode) {
        if (node.parentNode === target)
            return true;
        node=node.parentNode;
    }
    return false;
}

var container = document.getElementById("dropbox");
container.addEventListener("dragenter", function() {
    container.classList.add("dragging");
});

container.addEventListener("dragleave", function(e) {
    if (!isAncestor(e.relatedTarget, container))
        container.classList.remove("dragging");
});

你可以在这里找到一把能用的小提琴!

   private onDragLeave(e) {
     e.preventDefault();
     e.stopPropagation();

    if (this.isEventFromChild(e)) {
      return;
    }

    this.isDragOver = false;
  }
  
  private isEventFromChild(e): boolean {
    //relatedTarget is null on Safari, so we take it from coordinates
    const relatedTarget = e.relatedTarget || document.elementFromPoint(e.clientX, e.clientY);
    return e.currentTarget.contains(relatedTarget);
  }

问题是当鼠标移动到子元素前面时,dragleave事件被触发。

我已经尝试了各种方法来检查e.target元素是否与this元素相同,但没有任何改进。

我修复这个问题的方式有点hack,但工作100%。

dragleave: function(e) {
               // Get the location on screen of the element.
               var rect = this.getBoundingClientRect();

               // Check the mouseEvent coordinates are outside of the rectangle
               if(e.x > rect.left + rect.width || e.x < rect.left
               || e.y > rect.top + rect.height || e.y < rect.top) {
                   $(this).removeClass('red');
               }
           }

我找到了一个与@azlar的答案相似但更优雅的解决方案,这是我的解决方案:

$(document).on({
    dragenter: function(e) {
        e.stopPropagation();
        e.preventDefault();
        $("#dragging").show();
    },
    dragover: function(e) {
        e.stopPropagation();
        e.preventDefault();
    },
    dragleave: function(e) {
        e.stopPropagation();
        e.preventDefault();
        if (e.clientX <= 0 ||
            // compare clientX with the width of browser viewport
            e.clientX >= $(window).width() ||
            e.clientY <= 0 ||
            e.clientY >= $(window).height())
            $("#dragging").hide();
    }
});

此方法检测鼠标是否已离开页面。 它在Chrome和Edge上运行良好。

这是我的解决方案(https://jsfiddle.net/42mh0fd5/8):

<div id="droppable">
    <div id="overlay"></div>
    <a href="">test child 1</a>
    <br /><br />
    <button>test child 2</button>
    <br /><br />
    <button>test child 3</button>
    <br />
</div>
<p id="draggable" draggable="true">This element is draggable.</p>


<script type="text/javascript">
var dropElem = document.getElementById('droppable');
var overlayElem = document.getElementById('overlay');

overlayElem.addEventListener('drop', function(ev) {
    ev.preventDefault();
    console.log('drop', ev.dataTransfer.files)
    overlayElem.classList.remove('dragover')
    dropElem.classList.add('dropped')
    console.log('drop')
}, false);

overlayElem.addEventListener('dragover', function(ev) {
    ev.preventDefault();
}, false);

overlayElem.addEventListener('dragleave', function(ev) {
    console.log('dragleave')
    overlayElem.classList.remove('dragover')
}, false);

dropElem.addEventListener('dragenter', function(ev) {
    console.log('dragenter')
    overlayElem.classList.add('dragover')
}, false);
</script>

<style>
#draggable{
    padding:5px;
    background: #fec;
    display: inline-block;
}
#droppable{
    width: 300px;
    background: #eef;
    border: 1px solid #ccd;
    position: relative;
    text-align: center;
    padding:30px;
}
#droppable.dropped{
    background: #fee;
}
#overlay.dragover{
    content:"";
    position: absolute;
    z-index: 1;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background: rgba(0,0,0,.2);
    border:3px dashed #999;
}
</style>