我遇到的问题是,当悬停该元素的子元素时会触发元素的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");
}
});
它应该做的是通过在拖动某些东西时使拖放div
变为红色来通知用户。这样做有效,但是如果你拖入p
孩子,dragleave
会被触发,div
不再是红色。回到drop div
也不会再次变为红色。有必要完全移出掉落div
并再次拖回它以使其变红。
拖入子元素时是否可以阻止dragleave
触发?
2017年更新: TL; DR,查找CSS pointer-events: none;
,如下面@ H.D.的答案中所述,该答案适用于现代浏览器和IE11。
答案 0 :(得分:287)
你只需要保留一个参考计数器,当你得到一个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事件中,将counter重置为零,并清除添加的类。
您可以运行here
答案 1 :(得分:65)
拖入子元素时是否可以防止dragleave被触发?
是
#drop * {pointer-events: none;}
CSS似乎足以支持Chrome。
在Firefox中使用它时,#drop不应该直接有文本节点(否则有一个奇怪的issue where a element "leave it to itself"),所以我建议只留下一个元素(例如,在#drop中使用div)把一切都放在里面)
Here's a jsfiddle正在解决original question (broken) example。
我还从@Theodore Brown的例子中得到了一个simplified version,但只是在这个CSS中。
并非所有浏览器都实现了此CSS: http://caniuse.com/pointer-events
看到Facebook的源代码,我可以多次找到这个pointer-events: none;
,但它可能与优雅的降级后退一起使用。至少它是如此简单,并解决了很多环境的问题。
答案 2 :(得分:42)
这里是最简单的跨浏览器解决方案(严重):
jsfiddle&lt; - 尝试在框内拖动一些文件
你可以这样做:
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);
简而言之,你在dropzone中创建了一个“mask”,宽度和宽度都在高度继承,位置绝对,只会在dragover开始时显示 因此,在显示该面具之后,您可以通过附加其他拖拽和放大器来完成这一操作。放下它上面的事件。
离开或掉落后,您只需再次隐藏面具。
简单而无并发症。
(观察员:Greg Pettit建议 - 你必须确保面具悬停在整个盒子上,包括边框)
答案 3 :(得分:34)
解决此问题的“正确”方法是禁用放置目标的子元素上的指针事件(如@ H.D.的答案)。 Here's a jsFiddle I created which demonstrates this technique。不幸的是,这在IE11之前的Internet Explorer版本中不起作用,因为它们是didn't support pointer events。
幸运的是,我能够提出 在旧版IE中工作的解决方法。基本上,它涉及识别和忽略拖动子元素时发生的dragleave
事件。因为在父节点上的dragenter
事件之前在子节点上触发了dragleave
事件,所以可以向每个子节点添加单独的事件侦听器,从而添加或删除“ignore-drag-leave”类。放下目标。然后,放置目标的dragleave
事件侦听器可以简单地忽略此类存在时发生的调用。这是一个jsFiddle demonstrating this workaround。它经过测试,可在Chrome,Firefox和IE8 +中使用。
<强>更新强>
我使用功能检测创建了a jsFiddle demonstrating a combined solution,如果支持,则使用指针事件(当前是Chrome,Firefox和IE11),如果指针事件支持不可用,浏览器会回退到向子节点添加事件(IE8-10)。
答案 4 :(得分:26)
在提出这个问题并且提供了许多解决方案(包括丑陋的黑客攻击)之后已经有一段时间了。
由于此answer中的答案,我设法解决了我最近遇到的同样问题,并认为对于访问此页面的人可能会有所帮助。
整个想法是每次在任何父元素或子元素上调用时evenet.target
都存储ondrageenter
。然后在ondragleave
中检查当前目标(event.target
)是否等于ondragenter
中存储的对象。
这两个匹配的唯一情况是当您的拖动离开浏览器窗口时。
这种方法工作正常的原因是当鼠标离开元素(比如el1
)并输入另一个元素(比如说el2
)时,首先调用el1.ondragenter
,然后调用{{ 1}}。只有当拖动离开/进入浏览器窗口时,el2.ondragleave
和event.target
中的''
才会el1.ondragenter
。
这是我的工作样本。我已在IE9 +,Chrome,Firefox和Safari上测试过它。
el2.ondragleave
这是一个简单的html页面:
(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;
};
})();
通过正确的样式,我所做的就是在将文件拖入屏幕时使内部div(#file-drop-area)大得多,以便用户可以轻松地将文件放到适当的位置。
答案 5 :(得分:14)
问题是当鼠标移到子元素前面时会触发dragleave
事件。
我尝试了各种检查方法,以查看e.target
元素是否与this
元素相同,但无法获得任何改进。
我解决这个问题的方法有点像黑客,但是100%工作。
dragleave: function(e) {
// Get the location on screen of the element.
var rect = this.getBoundingClientRect();
// Check the mouseEvent coordinates are outside of the rectangle
if(e.x > rect.left + rect.width || e.x < rect.left
|| e.y > rect.top + rect.height || e.y < rect.top) {
$(this).removeClass('red');
}
}
答案 6 :(得分:9)
一个非常简单的解决方案是使用pointer-events
CSS property。只需在每个子元素上的 dragstart 上将其值设置为none
即可。这些元素不再触发与鼠标相关的事件,因此他们不会抓住鼠标,因此不会触发父母的 dragleave 。
完成拖动后,不要忘记将此属性设置回auto
;)
答案 7 :(得分:8)
您可以使用jQuery source code
中的一点灵感在Firefox中修复它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中不起作用,因为dragleave
事件中似乎不存在relatedTarget
,我认为您在Chrome中工作,因为您的示例在Firefox中不起作用。 Here's a version已实施上述代码。
答案 8 :(得分:7)
这就是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!');
}
})
答案 9 :(得分:6)
如果您使用的是HTML5,则可以获取父级的clientRect:
let rect = document.getElementById("drag").getBoundingClientRect();
然后在parent.dragleave()中:
dragleave(e) {
if(e.clientY < rect.top || e.clientY >= rect.bottom || e.clientX < rect.left || e.clientX >= rect.right) {
//real leave
}
}
答案 10 :(得分:6)
这是使用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');
}
}
希望这有效,这里是fiddle。
答案 11 :(得分:5)
我已经编写了一个名为Dragster的小库来处理这个问题,除了在IE中默默无效(不支持DOM事件构造函数,但是编写内容很容易)类似于使用jQuery的自定义事件)
答案 12 :(得分:5)
一个简单的解决方案是将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>
答案 13 :(得分:4)
我遇到了同样的问题并尝试使用pk7s解决方案。它有效,但没有任何额外的dom元素可以做得更好。
基本上这个想法是相同的 - 在可放置区域添加额外的不可见覆盖。只有这样做才能没有任何额外的dom元素。这部分内容是CSS伪元素发挥作用。
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);
此后规则将为可放置区域创建一个完全覆盖的叠加层。
#box.overlay:after {
content:'';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
}
以下是完整的解决方案:http://jsfiddle.net/F6GDq/8/
我希望它可以帮助任何有同样问题的人。
答案 14 :(得分:4)
不确定这是否是跨浏览器,但我在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;
});
答案 15 :(得分:3)
非常简单的解决方案:
parent.addEventListener('dragleave', function(evt) {
if (!parent.contains(evt.relatedTarget)) {
// Here it is only dragleave on the parent
}
}
答案 16 :(得分:3)
只检查被拖过的元素是否为子元素,如果是,则不要删除“dragover”样式类。非常简单,适合我:
$yourElement.on('dragleave dragend drop', function(e) {
if(!$yourElement.has(e.target).length){
$yourElement.removeClass('is-dragover');
}
})
答案 17 :(得分:3)
我偶然发现了同样的问题,这是我的解决方案 - 我认为这比上面容易得多。我不确定它是否是crossbrowser(可能依赖于冒泡的顺序)
为了简单起见,我将使用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)
就是这样。 :)它的工作原理是因为即使在onEave之前onEnter on on on父母,我们也会延迟它稍微颠倒顺序,所以先删除类然后在一个毫秒后重新获得。
答案 18 :(得分:2)
另一种工作解决方案,更简单一点。
//Note: Due to a bug with Chrome the 'dragleave' event is fired when hovering the dropzone, then
// we must check the mouse coordinates to be sure that the event was fired only when
// leaving the window.
//Facts:
// - [Firefox/IE] e.originalEvent.clientX < 0 when the mouse is outside the window
// - [Firefox/IE] e.originalEvent.clientY < 0 when the mouse is outside the window
// - [Chrome/Opera] e.originalEvent.clientX == 0 when the mouse is outside the window
// - [Chrome/Opera] e.originalEvent.clientY == 0 when the mouse is outside the window
// - [Opera(12.14)] e.originalEvent.clientX and e.originalEvent.clientY never get
// zeroed if the mouse leaves the windows too quickly.
if (e.originalEvent.clientX <= 0 || e.originalEvent.clientY <= 0) {
答案 19 :(得分:2)
花了这么多时间后,我得到了与预期完全一致的建议。我想在文件被拖过时提供一个提示,而文档dragover,dragleave在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
});
答案 20 :(得分:2)
我编写了一个名为drip-drop的拖放模块来修复这种古怪的行为。如果您正在寻找一个好的低级拖放模块,您可以使用它作为任何内容的基础(文件上传,应用程序内拖放,从外部源或从外部源拖动),您应该检查这个模块:
https://github.com/fresheneesz/drip-drop
这就是你在滴水中做的事情:
$('#drop').each(function(node) {
dripDrop.drop(node, {
enter: function() {
$(node).addClass('red')
},
leave: function() {
$(node).removeClass('red')
}
})
})
$('#drag').each(function(node) {
dripDrop.drag(node, {
start: function(setData) {
setData("text", "test") // if you're gonna do text, just do 'text' so its compatible with IE's awful and restrictive API
return "copy"
},
leave: function() {
$(node).removeClass('red')
}
})
})
为了在没有库的情况下执行此操作,计数器技术就是我在滴水中使用的,因为最高等级的答案错过了重要的步骤,这些步骤会导致除了第一滴之外的所有事情都会中断。以下是如何正确地做到这一点:
var counter = 0;
$('#drop').bind({
dragenter: function(ev) {
ev.preventDefault()
counter++
if(counter === 1) {
$(this).addClass('red')
}
},
dragleave: function() {
counter--
if (counter === 0) {
$(this).removeClass('red');
}
},
drop: function() {
counter = 0 // reset because a dragleave won't happen in this case
}
});
答案 21 :(得分:1)
“ dragleave ”事件。
这很有意义,因为在许多情况下,只有父母可以可丢弃,而不是后代。 我认为 event.stopPropogation()应该已经处理了这种情况,但似乎无法解决问题。
上文提到,某些解决方案似乎在大多数情况下都适用,但是对于那些不支持 dragenter / dragleave 事件的孩子,例如< strong> iframe 。
1解决方法是检查 event.relatedTarget 并验证其是否位于容器内,然后像我在此处所做的那样忽略 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");
});
您可以找到有效的小提琴here!
答案 22 :(得分:1)
到目前为止,假设您的事件分别附加到每个拖动元素,那么这个相当简单的解决方案对我来说仍然有效。
if (evt.currentTarget.contains(evt.relatedTarget)) {
return;
}
答案 23 :(得分:1)
解决了..!
为ex声明任何数组:
targetCollection : any[]
dragenter: function(e) {
this.targetCollection.push(e.target); // For each dragEnter we are adding the target to targetCollection
$(this).addClass('red');
},
dragleave: function() {
this.targetCollection.pop(); // For every dragLeave we will pop the previous target from targetCollection
if(this.targetCollection.length == 0) // When the collection will get empty we will remove class red
$(this).removeClass('red');
}
无需担心子元素。
答案 24 :(得分:1)
即使阅读了所有这些答案,我还是为此付出了很多努力,并认为我可以与您分享我的解决方案,因为我认为这可能是较为简单的方法之一,尽管有些不同。我的想法是完全省略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;
}
}
代码中留下了非常基本的注释,因此该代码也适合初学者。希望这对您有所帮助!请注意,您当然需要将我上面提到的所有事件侦听器添加到每个表格单元中,以使其起作用。
答案 25 :(得分:1)
我遇到了类似的问题 - 当我在Google Chrome浏览器中悬停使用dropzone闪烁的子元素时,我的代码用于在body的dragleave事件中隐藏dropzone的行为被触发了。
我能够通过安排隐藏dropzone的功能而不是立即调用它来解决这个问题。然后,如果触发另一个dragover或dragleave,则取消预定的函数调用。
body.addEventListener('dragover', function() {
clearTimeout(body_dragleave_timeout);
show_dropzone();
}, false);
body.addEventListener('dragleave', function() {
clearTimeout(body_dragleave_timeout);
body_dragleave_timeout = setTimeout(show_upload_form, 100);
}, false);
dropzone.addEventListener('dragover', function(event) {
event.preventDefault();
dropzone.addClass("hover");
}, false);
dropzone.addEventListener('dragleave', function(event) {
dropzone.removeClass("hover");
}, false);
答案 26 :(得分:1)
我找到了解决此问题的简单方法,因此将其共享。就我而言,效果很好。
jsfiddle尝试。
实际上,您只能通过dragenter
事件来实现此目的,甚至不需要注册dragleave
。您所需要的就是在您的拖放区周围有一个无拖放区,就是这样。
您也可以嵌套嵌套的dropzone,这非常有效。还要对此进行检查nested dropzones。
$('.dropzone').on("dragenter", function(e) {
e.preventDefault();
e.stopPropagation();
$(this).addClass("over");
$(".over").not(this).removeClass("over"); // in case of multiple dropzones
});
$('.dropzone-leave').on("dragenter", function(e) {
e.preventDefault();
e.stopPropagation();
$(".over").removeClass("over");
});
答案 27 :(得分:0)
这是我的解决方案(https://jsfiddle.net/42mh0fd5/8):
function toggleContent() {
var isFirstTabActive = firstTab.classList.contains("active");
firstTab.classList.toggle("active", !isFirstTabActive);
secondTab.classList.toggle("active", isFirstTabActive);
firstContent.classList.toggle("visible", !isFirstTabActive);
secondContent.classList.toggle("visible", isFirstTabActive);
}
答案 28 :(得分:0)
我能够在 dragleave
上使用超时来做到这一点。与此方法的其他答案不同,我认为在 dragover
上重置此超时以避免闪烁至关重要。
我正在编写您需要的函数,然后使用您选择的框架进行绑定
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);
}
}
答案 29 :(得分:0)
接受的答案可能在某些情况下有效,但如果您有多个重叠的子元素,它会很快崩溃。
根据您的用例,这篇文章的最佳答案可能更容易和更清晰
'dragleave' of parent element fires when dragging over children elements
答案 30 :(得分:0)
我找到了一个类似但更优雅的@ azlar答案解决方案,这是我的解决方案:
$(document).on({
dragenter: function(e) {
e.stopPropagation();
e.preventDefault();
$("#dragging").show();
},
dragover: function(e) {
e.stopPropagation();
e.preventDefault();
},
dragleave: function(e) {
e.stopPropagation();
e.preventDefault();
if (e.clientX <= 0 ||
// compare clientX with the width of browser viewport
e.clientX >= $(window).width() ||
e.clientY <= 0 ||
e.clientY >= $(window).height())
$("#dragging").hide();
}
});
此方法检测鼠标是否已离开页面。 它适用于Chrome和Edge。
答案 31 :(得分:0)
尝试使用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');
}
})
圭
答案 32 :(得分:0)
使用此代码http://jsfiddle.net/HU6Mk/258/:
$('#drop').bind({
dragenter: function() {
$(this).addClass('red');
},
dragleave: function(event) {
var x = event.clientX, y = event.clientY,
elementMouseIsOver = document.elementFromPoint(x, y);
if(!$(elementMouseIsOver).closest('.red').length) {
$(this).removeClass('red');
}
}
});
答案 33 :(得分:0)
您需要删除拖动目标的所有子对象的指针事件。
function disableChildPointerEvents(targetObj) {
var cList = parentObj.childNodes
for (i = 0; i < cList.length; ++i) {
try{
cList[i].style.pointerEvents = 'none'
if (cList[i].hasChildNodes()) disableChildPointerEvents(cList[i])
} catch (err) {
//
}
}
}
答案 34 :(得分:0)
您可以使用带有transitioning
标志的超时并侦听顶部元素。来自子事件的dragenter
/ dragleave
将冒泡到容器中。
由于子元素上的dragenter
在容器的dragleave
之前触发,我们将标志显示为转换为1ms ... dragleave
侦听器将检查之前的标志1ms就到了。
该标志仅在转换为子元素期间为真,并且在转换为(容器的)父元素时不会为真
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/
答案 35 :(得分:0)
pimvdb ..
为什么不尝试使用 drop 而不是 dragleave 。它对我有用。希望这能解决你的问题。
请检查jsFiddle:http://jsfiddle.net/HU6Mk/118/
$('#drop').bind({
dragenter: function() {
$(this).addClass('red');
},
drop: function() {
$(this).removeClass('red');
}
});
$('#drag').bind({
dragstart: function(e) {
e.allowedEffect = "copy";
e.setData("text/plain", "test");
}
});
答案 36 :(得分:0)
这是另一种基于事件发生时间的方法。
父元素可以捕获从子元素调度的dragenter
事件,它始终发生在dragleave
之前。这两个事件之间的时间非常短,比任何可能的人类鼠标动作都短。因此,我们的想法是记住dragenter
发生的时间并过滤dragleave
事件后发生的“不太快”......
这个简短的示例适用于Chrome和Firefox:
var node = document.getElementById('someNodeId'),
on = function(elem, evt, fn) { elem.addEventListener(evt, fn, false) },
time = 0;
on(node, 'dragenter', function(e) {
e.preventDefault();
time = (new Date).getTime();
// Drag start
})
on(node, 'dragleave', function(e) {
e.preventDefault();
if ((new Date).getTime() - time > 5) {
// Drag end
}
})