我遇到的问题是,一个元素的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中都能运行。


当前回答

一个简单的解决方案是向子组件添加css规则pointer-events: none,以防止触发ondragleave。看到的例子:

function enter(event) { document.querySelector('div').style.border = '1px dashed blue'; } function leave(event) { document.querySelector('div').style.border = ''; } div { border: 1px dashed silver; padding: 16px; margin: 8px; } article { border: 1px solid silver; padding: 8px; margin: 8px; } p { pointer-events: none; background: whitesmoke; } <article draggable="true">drag me</article> <div ondragenter="enter(event)" ondragleave="leave(event)"> drop here <p>child not triggering dragleave</p> </div>

其他回答

下面是另一个使用document.elementFromPoint的解决方案:

 dragleave: function(event) {
   var event = event.originalEvent || event;
   var newElement = document.elementFromPoint(event.pageX, event.pageY);
   if (!this.contains(newElement)) {
     $(this).removeClass('red');
   }
}

希望这有用,这是小提琴。

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

Yes.

#drop * {pointer-events: none;}

CSS似乎足够Chrome。

当使用它与Firefox, #drop不应该有文本节点直接(否则有一个奇怪的问题,一个元素“离开它自己”),所以我建议只留下一个元素(例如,使用一个div在#drop里面放所有的东西)

下面是一个jsfiddle解决原始问题(损坏的)示例。

我还从@Theodore Brown的例子中做了一个简化版本,但只基于这个CSS。

不过,并不是所有浏览器都实现了这种CSS: http://caniuse.com/pointer-events

看到Facebook的源代码,我可以找到这个指针事件:无;但是,它可能多次与优雅的降级回退一起使用。至少它是如此简单,解决了许多环境中的问题。

我找到了一个与@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上运行良好。

当鼠标指针离开目标容器的拖动区域时触发"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");
});

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

另一种工作解决方案,稍微简单一点。

//Note: Due to a bug with Chrome the 'dragleave' event is fired when hovering the dropzone, then
//      we must check the mouse coordinates to be sure that the event was fired only when 
//      leaving the window.
//Facts:
//  - [Firefox/IE] e.originalEvent.clientX < 0 when the mouse is outside the window
//  - [Firefox/IE] e.originalEvent.clientY < 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientX == 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientY == 0 when the mouse is outside the window
//  - [Opera(12.14)] e.originalEvent.clientX and e.originalEvent.clientY never get
//                   zeroed if the mouse leaves the windows too quickly.
if (e.originalEvent.clientX <= 0 || e.originalEvent.clientY <= 0) {