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


当前回答

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

试试吧。

你实际上可以通过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");
  }
});

其他回答

问题是当鼠标移动到子元素前面时,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');
               }
           }

你可以在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中不起作用。下面是实现上述代码的一个版本。

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

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

您需要删除拖动目标的所有子对象的指针事件。

function disableChildPointerEvents(targetObj) {
        var cList = parentObj.childNodes
        for (i = 0; i < cList.length; ++i) {
            try{
                cList[i].style.pointerEvents = 'none'
                if (cList[i].hasChildNodes()) disableChildPointerEvents(cList[i])
            } catch (err) {
                //
            }
        }
    }

不确定这是否跨浏览器,但我在Chrome中测试,它解决了我的问题:

我想拖放一个文件在整个页面,但我的dragleave被解雇时,我拖过子元素。我的解决方法是查看鼠标的x和y:

我有一个div覆盖我的整个页面,当页面加载我隐藏它。

当你拖拽文件时,我显示它,当你拖拽到父文件时,它处理它,当你离开父文件时,我检查x和y。

$('#draganddrop-wrapper').hide();

$(document).bind('dragenter', function(event) {
    $('#draganddrop-wrapper').fadeIn(500);
    return false;
});

$("#draganddrop-wrapper").bind('dragover', function(event) {
    return false;
}).bind('dragleave', function(event) {
    if( window.event.pageX == 0 || window.event.pageY == 0 ) {
        $(this).fadeOut(500);
        return false;
    }
}).bind('drop', function(event) {
    handleDrop(event);

    $(this).fadeOut(500);
    return false;
});