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


当前回答

非常简单的解决方案:

parent.addEventListener('dragleave', function(evt) {
    if (!parent.contains(evt.relatedTarget)) {
        // Here it is only dragleave on the parent
    }
}

其他回答

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

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

这是我的解决方案(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>

我知道这是一个老问题,但我想补充我的偏好。我处理这个通过添加类触发css:后元素在更高的z-index然后你的内容。这样可以过滤掉所有的垃圾。

.droppable{
    position: relative;
    z-index: 500;
}

.droppable.drag-over:after{
    content: "";
    display:block;
    position:absolute;
    left:0;
    right:0;
    top:0;
    bottom:0;
    z-index: 600;
}

然后只需在您的第一个dragenter事件上添加拖拽类,并且不再有子元素触发该事件。

dragEnter(event){
 dropElement.classList.add('drag-over');
}

dragLeave(event){
 dropElement.classList.remove('drag-over');
}

我找到了一个简单的解决方法,所以分享吧。这对我来说很有效。

试试吧。

你实际上可以通过dragenter事件来实现这一点,你甚至不需要注册一个dragleave。你所需要的就是在你的掉落区周围有一个不掉落的区域,就是这样。

你也可以有嵌套的dropzone,这是完美的。检查这个以及嵌套dropzones。

$('.dropzone').on("dragenter", function(e) {
  e.preventDefault();
  e.stopPropagation();
  $(this).addClass("over");
  $(".over").not(this).removeClass("over"); // in case of multiple dropzones
});

$('.dropzone-leave').on("dragenter", function(e) {
  e.preventDefault();
  e.stopPropagation();
  $(".over").removeClass("over");
});

// UPDATE
// As mar10 pointed out, the "Esc" key needs to be managed,
// the easiest approach is to detect the key and clean things up.

$(document).on('keyup', function(e){
  if (e.key === "Escape") {
    $(".over").removeClass("over");
  }
});

我也有同样的问题,并试图使用pk7s的解决方案。它工作,但它可以做得更好一点,没有任何额外的dom元素。

基本上想法是一样的-在可掉落的区域上添加一个额外的不可见的覆盖。让我们在没有任何额外dom元素的情况下这样做。下面是CSS伪元素开始发挥作用的部分。

Javascript

var dragOver = function (e) {
    e.preventDefault();
    this.classList.add('overlay');
};

var dragLeave = function (e) {
    this.classList.remove('overlay');
};


var dragDrop = function (e) {
    this.classList.remove('overlay');
    window.alert('Dropped');
};

var dropArea = document.getElementById('box');

dropArea.addEventListener('dragover', dragOver, false);
dropArea.addEventListener('dragleave', dragLeave, false);
dropArea.addEventListener('drop', dragDrop, false);

CSS

这个after规则将为可掉落区域创建一个完全覆盖的覆盖层。

#box.overlay:after {
    content:'';
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 1;
}

这里是完整的解决方案:http://jsfiddle.net/F6GDq/8/

我希望它能帮助有同样问题的人。