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


当前回答

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

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

其他回答

下面是Chrome的解决方案:

.bind('dragleave', function(event) {
                    var rect = this.getBoundingClientRect();
                    var getXY = function getCursorPosition(event) {
                        var x, y;

                        if (typeof event.clientX === 'undefined') {
                            // try touch screen
                            x = event.pageX + document.documentElement.scrollLeft;
                            y = event.pageY + document.documentElement.scrollTop;
                        } else {
                            x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                            y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
                        }

                        return { x: x, y : y };
                    };

                    var e = getXY(event.originalEvent);

                    // Check the mouseEvent coordinates are outside of the rectangle
                    if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
                        console.log('Drag is really out of area!');
                    }
                })

我也有同样的问题,并试图使用pk7s的解决方案。它工作,但它可以做得更好一点,没有任何额外的dom元素。

基本上想法是一样的-在可掉落的区域上添加一个额外的不可见的覆盖。让我们在没有任何额外dom元素的情况下这样做。下面是CSS伪元素开始发挥作用的部分。

Javascript

var dragOver = function (e) {
    e.preventDefault();
    this.classList.add('overlay');
};

var dragLeave = function (e) {
    this.classList.remove('overlay');
};


var dragDrop = function (e) {
    this.classList.remove('overlay');
    window.alert('Dropped');
};

var dropArea = document.getElementById('box');

dropArea.addEventListener('dragover', dragOver, false);
dropArea.addEventListener('dragleave', dragLeave, false);
dropArea.addEventListener('drop', dragDrop, false);

CSS

这个after规则将为可掉落区域创建一个完全覆盖的覆盖层。

#box.overlay:after {
    content:'';
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 1;
}

这里是完整的解决方案:http://jsfiddle.net/F6GDq/8/

我希望它能帮助有同样问题的人。

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

解决这个问题的“正确”方法是禁用拖放目标的子元素上的指针事件(如@ h.d.。的回答)。下面是我创建的演示这种技术的jsFiddle。不幸的是,这在IE11之前的ie版本中不起作用,因为它们不支持指针事件。

Luckily, I was able to come up with a workaround which does work in old versions of IE. Basically, it involves identifying and ignoring dragleave events which occur when dragging over child elements. Because the dragenter event is fired on child nodes before the dragleave event on the parent, separate event listeners can be added to each child node which add or remove an "ignore-drag-leave" class from the drop target. Then the drop target's dragleave event listener can simply ignore calls which occur when this class exists. Here's a jsFiddle demonstrating this workaround. It is tested and working in Chrome, Firefox, and IE8+.

更新:

我创建了一个jsFiddle,演示了一个使用特性检测的组合解决方案,其中如果支持指针事件,则使用指针事件(目前是Chrome、Firefox和IE11),如果指针事件支持不可用,则浏览器退回到向子节点添加事件(IE8-10)。

在这个问题被提出之后已经有一段时间了,并且提供了很多解决方案(包括丑陋的黑客)。

我设法解决了我最近遇到的同样的问题,感谢这个答案中的答案,并认为它可能对来到这个页面的人有帮助。 整个思想就是存储事件。每当对任何父元素或子元素调用时,ondrageenter中的目标。然后在ondragleave检查当前目标(event.target)是否等于你在ondragenter中存储的对象。

这两者匹配的唯一情况是当您的拖动离开浏览器窗口时。

这样工作正常的原因是当鼠标离开一个元素(例如el1)并进入另一个元素(例如el2)时,首先是el2。调用Ondragenter,然后调用el1.ondragleave。仅当拖动离开/进入浏览器窗口时,事件。目标将是“在两个el2。Ondragenter和el1.ondragleave。

这是我的工作样本。我已经在IE9+、Chrome、Firefox和Safari上进行了测试。

(function() {
    var bodyEl = document.body;
    var flupDiv = document.getElementById('file-drop-area');

    flupDiv.onclick = function(event){
        console.log('HEy! some one clicked me!');
    };

    var enterTarget = null;

    document.ondragenter = function(event) {
        console.log('on drag enter: ' + event.target.id);
        enterTarget = event.target;
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-drag-on-top';
        return false;
    };

    document.ondragleave = function(event) {
        console.log('on drag leave: currentTarget: ' + event.target.id + ', old target: ' + enterTarget.id);
        //Only if the two target are equal it means the drag has left the window
        if (enterTarget == event.target){
            event.stopPropagation();
            event.preventDefault();
            flupDiv.className = 'flup-no-drag';         
        }
    };
    document.ondrop = function(event) {
        console.log('on drop: ' + event.target.id);
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-no-drag';
        return false;
    };
})();

这是一个简单的html页面:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multiple File Uploader</title>
<link rel="stylesheet" href="my.css" />
</head>
<body id="bodyDiv">
    <div id="cntnr" class="flup-container">
        <div id="file-drop-area" class="flup-no-drag">blah blah</div>
    </div>
    <script src="my.js"></script>
</body>
</html>

使用适当的样式,我所做的是使内部div (#file-drop area)在文件被拖到屏幕时变得更大,以便用户可以轻松地将文件放到适当的位置。