我遇到的问题是,一个元素的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在拖动到子元素时发射?

Yes.

#drop * {pointer-events: none;}

CSS似乎足够Chrome。

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

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

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

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

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

其他回答

在花了这么多小时后,我得到的建议完全按照预期工作。我只想在文件被拖拽时提供提示,而文档拖拽,拖拽会在Chrome浏览器上引起痛苦的闪烁。

这就是我解决它的方法,也为用户提供了适当的提示。

$(document).on('dragstart dragenter dragover', function(event) {    
    // Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
    if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
        // Needed to allow effectAllowed, dropEffect to take effect
        event.stopPropagation();
        // Needed to allow effectAllowed, dropEffect to take effect
        event.preventDefault();

        $('.dropzone').addClass('dropzone-hilight').show();     // Hilight the drop zone
        dropZoneVisible= true;

        // http://www.html5rocks.com/en/tutorials/dnd/basics/
        // http://api.jquery.com/category/events/event-object/
        event.originalEvent.dataTransfer.effectAllowed= 'none';
        event.originalEvent.dataTransfer.dropEffect= 'none';

         // .dropzone .message
        if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
            event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
            event.originalEvent.dataTransfer.dropEffect= 'move';
        } 
    }
}).on('drop dragleave dragend', function (event) {  
    dropZoneVisible= false;

    clearTimeout(dropZoneTimer);
    dropZoneTimer= setTimeout( function(){
        if( !dropZoneVisible ) {
            $('.dropzone').hide().removeClass('dropzone-hilight'); 
        }
    }, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
});

我也遇到过同样的问题,下面是我的解决方案——我认为比上面的要简单得多。我不确定它是否跨浏览器(可能取决于冒泡顺序)

为了简单起见,我将使用jQuery,但解决方案应该是框架独立的。

事件以任意一种方式生成父节点,如下所示:

<div class="parent">Parent <span>Child</span></div>

我们附加事件

el = $('.parent')
setHover = function(){ el.addClass('hovered') }
onEnter  = function(){ setTimeout(setHover, 1) }
onLeave  = function(){ el.removeClass('hovered') } 
$('.parent').bind('dragenter', onEnter).bind('dragleave', onLeave)

差不多就是这样。:)它可以工作,因为即使onEnter在子上先于onLeave在父上触发,我们延迟它稍微颠倒顺序,所以类先被删除,然后在一毫秒后重新应用。

接受的答案在某些情况下可能有效,但如果您有多个重叠的子元素,它将很快崩溃。

根据您的用例,本文的顶部答案可能更简单、更清晰

当拖动子元素时,父元素的'dragleave'会触发

我能够做到这一点使用超时dragleave。与其他使用这种方法的答案不同,我认为在拖拽时重置这个超时以避免闪烁是至关重要的。

我写的只是你需要的函数,然后使用你选择的框架进行绑定

let dragoverTimeout;
const onDragOver = (e: Event) => {
  e.preventDefault();
  e.stopPropagation();

  if (dragoverTimeout) {
    window.clearTimeout(dragoverTimeout);
    dragoverTimeout = null;
  }
  // Add your class here
}

const onDragLeave = (e: Event) => {
  e.preventDefault();
  e.stopPropagation();

  if (!dragoverTimeout) {
    dragoverTimeout = window.setTimeout(() => {
      // Remove your class here
    }, 100);
  }
}

const onDrop = (e) => {
  e.preventDefault();
  e.stopPropagation();

  const files = e.dataTransfer.files;
  // Remove your class here

  if (files.length > 0) {
    this.uploadFile(files);
  }
}

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