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


当前回答

一个非常简单的解决方案是使用CSS属性pointer-events。只需在拖拽每个子元素时将其值设置为none。这些元素将不再触发与鼠标相关的事件,因此它们将不会捕获鼠标在其上,从而不会触发父元素上的dragleave。

不要忘记在完成拖动时将此属性设置为auto;)

其他回答

是否有可能阻止dragleave在拖动到子元素时发射?

Yes.

#drop * {pointer-events: none;}

CSS似乎足够Chrome。

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

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

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

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

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

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

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

我在这个问题上挣扎了很多,即使在读完所有这些答案之后,我想我可以和你分享我的解决方案,因为我认为这可能是一种更简单的方法,尽管有点不同。我的想法是简单地完全省略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;
  }
}

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

试着使用event。eventphase。只有输入目标时,它才会被设置为2 (Event.AT_TARGET),否则它会被设置为3 (Event.BUBBLING_PHASE)。

我已经使用eventPhase来绑定或取消绑定dragleave事件。

$('.dropzone').on('dragenter', function(e) {

  if(e.eventPhase === Event.AT_TARGET) {

    $('.dropzone').addClass('drag-over');

    $('.dropzone').on('dragleave', function(e) {
      $('.dropzone').removeClass('drag-over');
    });

  }else{

    $('.dropzone').off('dragleave');

  }
})

圭多

您可以使用带有转换标志的超时并监听顶部元素。子事件中的Dragenter / dragleave将会弹出到容器中。

由于子元素的dragenter在容器的dragleave之前触发,我们将把标志显示设置为过渡1ms…dragleave监听器将在1毫秒结束之前检查标志。

该标志仅在转换到子元素时为真,而在转换到(容器的)父元素时为假。

var $el = $('#drop-container'),
    transitioning = false;

$el.on('dragenter', function(e) {

  // temporarily set the transitioning flag for 1 ms
  transitioning = true;
  setTimeout(function() {
    transitioning = false;
  }, 1);

  $el.toggleClass('dragging', true);

  e.preventDefault();
  e.stopPropagation();
});

// dragleave fires immediately after dragenter, before 1ms timeout
$el.on('dragleave', function(e) {

  // check for transitioning flag to determine if were transitioning to a child element
  // if not transitioning, we are leaving the container element
  if (transitioning === false) {
    $el.toggleClass('dragging', false);
  }

  e.preventDefault();
  e.stopPropagation();
});

// to allow drop event listener to work
$el.on('dragover', function(e) {
  e.preventDefault();
  e.stopPropagation();
});

$el.on('drop', function(e) {
  alert("drop!");
});

jsfiddle: http://jsfiddle.net/ilovett/U7mJj/