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


当前回答

我写了一个叫做Dragster的小库来处理这个确切的问题,除了在IE中默默不做任何事情外,它可以在任何地方工作(IE不支持DOM事件构造器,但使用jQuery的自定义事件很容易写一些类似的东西)

其他回答

你可以在Firefox中使用jQuery源代码来修复它:

dragleave: function(e) {
    var related = e.relatedTarget,
        inside = false;

    if (related !== this) {

        if (related) {
            inside = jQuery.contains(this, related);
        }

        if (!inside) {

            $(this).removeClass('red');
        }
    }

}

不幸的是,它在Chrome中不起作用,因为relatedTarget在dragleave事件上似乎不存在,我假设你在Chrome中工作,因为你的例子在Firefox中不起作用。下面是实现上述代码的一个版本。

我有一个类似的问题-我的代码隐藏dropzone dragleave事件的身体被触发时悬停子元素使dropzone闪烁在谷歌Chrome。

我能够通过调度隐藏dropzone的函数来解决这个问题,而不是立即调用它。然后,如果触发另一个拖拽或拖拽,则取消预定的函数调用。

body.addEventListener('dragover', function() {
    clearTimeout(body_dragleave_timeout);
    show_dropzone();
}, false);

body.addEventListener('dragleave', function() {
    clearTimeout(body_dragleave_timeout);
    body_dragleave_timeout = setTimeout(show_upload_form, 100);
}, false);

dropzone.addEventListener('dragover', function(event) {
    event.preventDefault();
    dropzone.addClass("hover");
}, false);

dropzone.addEventListener('dragleave', function(event) {
    dropzone.removeClass("hover");
}, false);

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

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

下面是另一个使用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');
   }
}

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

到目前为止,这个相当简单的解决方案对我来说是有效的,假设您的事件分别附加到每个拖动元素。

if (evt.currentTarget.contains(evt.relatedTarget)) {
  return;
}