jQuery 拖动子元素时父元素的“dragleave”触发
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10867506/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
'dragleave' of parent element fires when dragging over children elements
提问by Hristo
Overview
概述
I have the following HTML structure and I've attached the dragenter
and dragleave
events to the <div id="dropzone">
element.
我有以下 HTML 结构,并且已将dragenter
和dragleave
事件附加到<div id="dropzone">
元素。
<div id="dropzone">
<div id="dropzone-content">
<div id="drag-n-drop">
<div class="text">this is some text</div>
<div class="text">this is a container with text and images</div>
</div>
</div>
</div>
Problem
问题
When I drag a file over the <div id="dropzone">
, the dragenter
event is fired as expected. However, when I move my mouse over a child element, such as <div id="drag-n-drop">
, the dragenter
event is fired for the <div id="drag-n-drop">
element and then the dragleave
event is fired for the <div id="dropzone">
element.
当我将文件拖到 上时<div id="dropzone">
,dragenter
会按预期触发该事件。但是,当我将鼠标移到子元素上时,例如<div id="drag-n-drop">
,会dragenter
为该<div id="drag-n-drop">
元素dragleave
触发事件,然后为该<div id="dropzone">
元素触发该事件。
If I hover over the <div id="dropzone">
element again, the dragenter
event is again fired, which is cool, but then the dragleave
event is fired for the child element just left, so the removeClass
instruction is executed, which is not cool.
如果我<div id="dropzone">
再次将鼠标悬停在该元素上,dragenter
则再次触发该事件,这很酷,但是随后dragleave
为刚刚离开的子元素触发了该事件,因此removeClass
执行了指令,这很酷。
This behavior is problematic for 2 reasons:
这种行为有问题有两个原因:
I'm only attaching
dragenter
&dragleave
to the<div id="dropzone">
so I don't understand why the children elements have these events attached as well.I'm still dragging over the
<div id="dropzone">
element while hovering over its children so I don't wantdragleave
to fire!
我只是将
dragenter
&附加dragleave
到,<div id="dropzone">
所以我不明白为什么子元素也附加了这些事件。我仍然在将
<div id="dropzone">
鼠标悬停在其子元素上的同时拖动元素,所以我不想dragleave
开火!
jsFiddle
js小提琴
Here's a jsFiddle to tinker with: http://jsfiddle.net/yYF3S/2/
这是一个 jsFiddle 来修补:http: //jsfiddle.net/yYF3S/2/
Question
题
So... how can I make it such that when I'm dragging a file over the <div id="dropzone">
element, dragleave
doesn't fire even if I'm dragging over any children elements... it should only fire when I leave the <div id="dropzone">
element... hovering/dragging around anywhere within the boundaries of the element should nottrigger the dragleave
event.
所以......我怎样才能做到这样,当我将文件拖到<div id="dropzone">
元素上时,dragleave
即使我拖过任何子元素也不会触发......它应该只在我离开<div id="dropzone">
元素时触发.. . 在元素边界内的任何地方悬停/拖动不应触发dragleave
事件。
I need this to be cross-browser compatible, at least in the browsers that support HTML5 drag-n-drop, so this answeris not adequate.
我需要它是跨浏览器兼容的,至少在支持 HTML5 拖放的浏览器中,所以这个答案是不够的。
It seems like Google and Dropbox have figured this out, but their source code is minified/complex so I haven't been able to figure this out from their implementation.
似乎 Google 和 Dropbox 已经解决了这个问题,但是他们的源代码被缩小/复杂,所以我无法从他们的实现中弄清楚这一点。
采纳答案by Hristo
I finally found a solution I'm happy with. I actually found several ways to do what I want but none were as successful as the current solution... in one solution, I experienced frequent flickering as a result of adding/removing a border to the #dropzone
element... in another, the border was never removed if you hover away from the browser.
我终于找到了我满意的解决方案。我实际上找到了几种方法来做我想做的事,但没有一种方法能像当前的解决方案一样成功......在一个解决方案中,由于向#dropzone
元素添加/删除边框,我经历了频繁的闪烁......在另一个解决方案中,边框如果您将鼠标悬停在浏览器之外,则永远不会被删除。
Anyway, my best hacky solution is this:
无论如何,我最好的解决方案是这样的:
var dragging = 0;
attachEvent(window, 'dragenter', function(event) {
dragging++;
$(dropzone).addClass('drag-n-drop-hover');
event.stopPropagation();
event.preventDefault();
return false;
});
attachEvent(window, 'dragover', function(event) {
$(dropzone).addClass('drag-n-drop-hover');
event.stopPropagation();
event.preventDefault();
return false;
});
attachEvent(window, 'dragleave', function(event) {
dragging--;
if (dragging === 0) {
$(dropzone).removeClass('drag-n-drop-hover');
}
event.stopPropagation();
event.preventDefault();
return false;
});
This works pretty well but issues came up in Firefox because Firefox was double-invoking dragenter
so my counter was off. But nevertheless, its not a very elegant solution.
这很有效,但在 Firefox 中出现了问题,因为 Firefox 是双重调用的,dragenter
所以我的计数器关闭了。但是,它不是一个非常优雅的解决方案。
Then I stumbled upon this question: How to detect the dragleave event in Firefox when dragging outside the window
然后我偶然发现了这个问题:How to detection the dragleave event in Firefox when dragging out of window
So I took the answerand applied it to my situation:
所以我接受了答案并将其应用于我的情况:
$.fn.dndhover = function(options) {
return this.each(function() {
var self = $(this);
var collection = $();
self.on('dragenter', function(event) {
if (collection.size() === 0) {
self.trigger('dndHoverStart');
}
collection = collection.add(event.target);
});
self.on('dragleave', function(event) {
/*
* Firefox 3.6 fires the dragleave event on the previous element
* before firing dragenter on the next one so we introduce a delay
*/
setTimeout(function() {
collection = collection.not(event.target);
if (collection.size() === 0) {
self.trigger('dndHoverEnd');
}
}, 1);
});
});
};
$('#dropzone').dndhover().on({
'dndHoverStart': function(event) {
$('#dropzone').addClass('drag-n-drop-hover');
event.stopPropagation();
event.preventDefault();
return false;
},
'dndHoverEnd': function(event) {
$('#dropzone').removeClass('drag-n-drop-hover');
event.stopPropagation();
event.preventDefault();
return false;
}
});
This is clean and elegant and seems to be working in every browser I've tested so far (haven't tested IE yet).
这是干净和优雅的,似乎在我迄今为止测试过的每个浏览器中都可以使用(还没有测试过 IE)。
回答by Ben Rudolph
If you don't need to bind events to the child elements, you can always use the pointer-events property.
如果您不需要将事件绑定到子元素,您始终可以使用 pointer-events 属性。
.child-elements {
pointer-events: none;
}
回答by hacklikecrack
This is a little ugly but it works dammit!...
这有点难看,但它确实有效!...
On your 'dragenter' handler store the event.target (in a variable inside your closure, or whatever), then in your 'dragleave' handler only fire your code if event.target === the one you stored.
在您的 'dragenter' 处理程序上存储 event.target (在您的闭包内的变量中,或其他任何地方),然后在您的 'dragleave' 处理程序中,如果 event.target === 您存储的那个,则仅触发您的代码。
If your 'dragenter' is firing when you don't want it to (i.e. when it's entering after leaving child elements), then the last time it fires before the mouse leaves the parent, it's on the parent, so the parent will always be the final 'dragenter' before the intended 'dragleave'.
如果您的“dragenter”在您不希望的时候触发(即在离开子元素后进入时),那么最后一次在鼠标离开父元素之前触发时,它在父元素上,因此父元素将始终为预期的“dragleave”之前的最后一个“dragenter”。
(function () {
var droppable = $('#droppable'),
lastenter;
droppable.on("dragenter", function (event) {
lastenter = event.target;
droppable.addClass("drag-over");
});
droppable.on("dragleave", function (event) {
if (lastenter === event.target) {
droppable.removeClass("drag-over");
}
});
}());
回答by bargar
At first, I agreed with folks discarding the pointer-events: none
approach. But then I asked myself:
起初,我同意人们放弃这种pointer-events: none
方法。但后来我问自己:
Do you really need pointer-events to work on the child elements while dragging is in progress?
在拖动过程中,您真的需要指针事件来处理子元素吗?
In my case, I have lots of stuff going on in the children, e.g. hover to show buttons for additional actions, inline-editing, etc... However, noneof that is necessary or in fact even desired duringa drag.
就我而言,我在子项中进行了很多操作,例如悬停以显示用于附加操作的按钮、内联编辑等……但是,在拖动过程中,这些都不是必需的,甚至实际上是不需要的。
In my case, I use something like this to turn pointer events off selectively for all child nodes of the parent container:
在我的例子中,我使用这样的东西来为父容器的所有子节点有选择地关闭指针事件:
div.drag-target-parent-container.dragging-in-progress * {
pointer-events: none;
}
Use your favorite approach to add/remove the class dragging-in-progress
in the dragEnter
/dragLeave
event handlers, as I did or do the same in dragStart
, et. al.
使用您最喜欢的方法dragging-in-progress
在dragEnter
/dragLeave
事件处理程序中添加/删除类,就像我在dragStart
等中所做的或做的一样。阿尔。
回答by Blender
This seems to be a Chrome bug.
这似乎是一个 Chrome 错误。
The only workaround that I could think of was to create a transparent overlay element to capture your events: http://jsfiddle.net/yYF3S/10/
我能想到的唯一解决方法是创建一个透明的覆盖元素来捕获您的事件:http: //jsfiddle.net/yYF3S/10/
JS:
JS:
$(document).ready(function() {
var dropzone = $('#overlay');
dropzone.on('dragenter', function(event) {
$('#dropzone-highlight').addClass('dnd-hover');
});
dropzone.on('dragleave', function(event) {
$('#dropzone-highlight').removeClass('dnd-hover');
});
});?
HTML:
HTML:
<div id="dropzone-highlight">
<div id="overlay"></div>
<div id="dropzone" class="zone">
<div id="drag-n-drop">
<div class="text1">this is some text</div>
<div class="text2">this is a container with text and images</div>
</div>
</div>
</div>
<h2 draggable="true">Drag me</h2>
?
回答by Oliver
The problem is, that your elements inside the dropzones are of course part of the dropzone and when you enter the children, you leave the parent. Solving this is not easy. You might try adding events to the children too adding your class again to the parent.
问题是,dropzones 内的元素当然是 dropzone 的一部分,当你进入子元素时,你离开了父元素。解决这个问题并不容易。您可以尝试向孩子添加事件,也可以将您的班级再次添加到父级。
$("#dropzone,#dropzone *").on('dragenter', function(event) {
// add a class to #dropzone
event.stopPropagation(); // might not be necessary
event.preventDefault();
return false;
});
Your events will still fire multiple times but nobody will see.
您的事件仍会多次触发,但没有人会看到。
//Edit: Use the dragmove event to permanently overwrite the dragleave event:
//编辑:使用dragmove事件永久覆盖dragleave事件:
$("#dropzone,#dropzone *").on('dragenter dragover', function(event) {
// add a class to #dropzone
event.stopPropagation(); // might not be necessary
event.preventDefault();
return false;
});
Define the dragleave event only for the dropzone.
仅为放置区定义 dragleave 事件。
回答by Vahid Ashrafian
As benrmentioned in this answer, you can prevent child nodes to fire on events, but if you need to bind some events, do this:
正如benr中提到的这个答案,就可以防止子节点火事件,但如果你需要绑定一些事件,这样做:
#dropzone.dragover *{
pointer-events: none;
}
And add this one to your JS code:
并将此添加到您的 JS 代码中:
$("#dropzone").on("dragover", function (event) {
$("#dropzone").addClass("dragover");
});
$("#dropzone").on("dragleave", function (event) {
$("#dropzone").removeClass("dragover");
});
回答by nkkollaw
If you're using jQuery, check this out: https://github.com/dancork/jquery.event.dragout
如果您使用 jQuery,请查看:https: //github.com/dancork/jquery.event.dragout
It's truly awesome.
真是太棒了。
Special event created to handle true dragleave functionality.
HTML5 dragleave event works more like mouseout. This plugin was created to replicate the mouseleave style functionality whilst dragging.
Usage Example:
$('#myelement').on('dragout',function(event){ // YOUR CODE });
为处理真正的拖拽功能而创建的特殊事件。
HTML5 dragleave 事件更像 mouseout。创建此插件是为了在拖动时复制 mouseleave 样式功能。
用法示例:
$('#myelement').on('dragout',function(event){ // 你的代码 });
EDIT: actually, I don't think it's dependent on jQuery, you can probably just use the code even without it.
编辑:实际上,我认为它不依赖于 jQuery,即使没有它,您也可以只使用代码。
回答by visitsb
@hristo I have a much more elegant solution. Check that if that is something you could use.
@hristo 我有一个更优雅的解决方案。检查这是否是您可以使用的东西。
Your effort was not wasted after all. I managed to use yours at first, but had different problems in FF, Chrome. After spending so many hours I got that suggestion working exactly as intended.
毕竟你的努力没有白费。一开始我设法使用了你的,但在 FF、Chrome 中遇到了不同的问题。花了这么多小时后,我得到了完全按预期工作的建议。
Here's how the implementation is. I also took advantage of visual cues to correctly guide users about the drop zone.
这是实现的方式。我还利用视觉提示正确引导用户了解放置区。
$(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
});
回答by jeremie.b
My two cents: Hide a layer over your dropzone then show it when you dragenter, and target the dragleave on it.
我的两分钱:在你的 dropzone 上隐藏一个图层,然后在你 dragenter 时显示它,并在它上面瞄准 dragleave。
Demo : https://jsfiddle.net/t6q4shat/
演示:https: //jsfiddle.net/t6q4shat/
HTML
HTML
<div class="drop-zone">
<h2 class="drop-here">Drop here</h2>
<h2 class="drop-now">Drop now!</h2>
<p>Or <a href="#">browse a file</a></p>
<div class="drop-layer"></div>
</div>
CSS
CSS
.drop-zone{
padding:50px;
border:2px dashed #999;
text-align:center;
position:relative;
}
.drop-layer{
display:none;
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
z-index:5;
}
.drop-now{
display:none;
}
JS
JS
$('.drop-zone').on('dragenter', function(e){
$('.drop-here').css('display','none');
$('.drop-now').css('display','block');
$(this).find('.drop-layer').css('display','block');
return false;
});
$('.drop-layer').on('dragleave', function(e){
$('.drop-here').css('display','block');
$('.drop-now').css('display','none');
$(this).css('display','none');
return false;
});