我遇到的问题是,一个元素的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的自定义事件很容易写一些类似的东西)

其他回答

我在这个问题上挣扎了很多,即使在读完所有这些答案之后,我想我可以和你分享我的解决方案,因为我认为这可能是一种更简单的方法,尽管有点不同。我的想法是简单地完全省略dragleave事件监听器,并在每个新的dragenter事件触发时编码dragleave行为,同时确保dragenter事件不会被不必要地触发。

在下面的例子中,我有一个表,我希望能够通过拖放API相互交换表行内容。在dragenter上,一个CSS类将被添加到当前拖动元素的row元素中,以突出显示它,而在dragleave上,这个类将被删除。

例子:

非常基本的HTML表格:

<table>
  <tr>
    <td draggable="true" class="table-cell">Hello</td>
  </tr>
  <tr>
    <td draggable="true" clas="table-cell">There</td>
  </tr>
</table>

还有dragenter事件处理函数,添加到每个表格单元格中(除了dragstart, dragover, drop和dragend处理程序,它们不是针对这个问题的,所以这里不复制):

/*##############################################################################
##                              Dragenter Handler                             ##
##############################################################################*/

// When dragging over the text node of a table cell (the text in a table cell),
// while previously being over the table cell element, the dragleave event gets
// fired, which stops the highlighting of the currently dragged cell. To avoid
// this problem and any coding around to fight it, everything has been
// programmed with the dragenter event handler only; no more dragleave needed

// For the dragenter event, e.target corresponds to the element into which the
// drag enters. This fact has been used to program the code as follows:

var previousRow = null;

function handleDragEnter(e) {
  // Assure that dragenter code is only executed when entering an element (and
  // for example not when entering a text node)
  if (e.target.nodeType === 1) {
    // Get the currently entered row
    let currentRow = this.closest('tr');
    // Check if the currently entered row is different from the row entered via
    // the last drag
    if (previousRow !== null) {
      if (currentRow !== previousRow) {
        // If so, remove the class responsible for highlighting it via CSS from
        // it
        previousRow.className = "";
      }
    }
    // Each time an HTML element is entered, add the class responsible for
    // highlighting it via CSS onto its containing row (or onto itself, if row)
    currentRow.className = "ready-for-drop";
    // To know which row has been the last one entered when this function will
    // be called again, assign the previousRow variable of the global scope onto
    // the currentRow from this function run
    previousRow = currentRow;
  }
}

在代码中留下非常基本的注释,因此这段代码也适合初学者。希望这对你有所帮助!请注意,您当然需要将我上面提到的所有事件侦听器添加到每个表单元格中,这样才能工作。

你只需要保留一个引用计数器,当你得到dragenter时增加它,当你得到dragleave时减少它。当计数器为0时-删除类。

var counter = 0;

$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault(); // needed for IE
        counter++;
        $(this).addClass('red');
    },

    dragleave: function() {
        counter--;
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    }
});

注意:在drop事件中,将计数器重置为零,并清除添加的类。

你可以在这里运行它

在花了这么多小时后,我得到的建议完全按照预期工作。我只想在文件被拖拽时提供提示,而文档拖拽,拖拽会在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
});

不确定这是否跨浏览器,但我在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;
});

这里是最简单的跨浏览器解决方案(认真地说):

Jsfiddle <——尝试将一些文件拖到框中

你可以这样做:

var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');

dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);

简单地说,你在拖拽区域内创建了一个“蒙版”,宽度和高度是继承的,位置是绝对的,这将显示拖拽开始的时间。 因此,在显示该蒙版之后,您可以通过附加其他拖放事件来实现这一技巧。

离开或掉落后,你只需将面具再次隐藏起来。 简单,无并发症。

(奥林匹克广播服务公司。: Greg Pettit的建议——你必须确保蒙版悬浮在整个盒子上,包括边框)