我遇到的问题是,一个元素的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
    }
}

其他回答

一个简单的解决方案是向子组件添加css规则pointer-events: none,以防止触发ondragleave。看到的例子:

function enter(event) { document.querySelector('div').style.border = '1px dashed blue'; } function leave(event) { document.querySelector('div').style.border = ''; } div { border: 1px dashed silver; padding: 16px; margin: 8px; } article { border: 1px solid silver; padding: 8px; margin: 8px; } p { pointer-events: none; background: whitesmoke; } <article draggable="true">drag me</article> <div ondragenter="enter(event)" ondragleave="leave(event)"> drop here <p>child not triggering dragleave</p> </div>

你只需要保留一个引用计数器,当你得到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事件中,将计数器重置为零,并清除添加的类。

你可以在这里运行它

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

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

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

为了简单起见,我将使用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在父上触发,我们延迟它稍微颠倒顺序,所以类先被删除,然后在一毫秒后重新应用。

您可以使用带有转换标志的超时并监听顶部元素。子事件中的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/