在窗口外拖动时如何检测Firefox中的dragleave事件
Firefox 在窗口外拖动时没有正确触发 dragleave 事件:
Firefox doesn't properly trigger the dragleave event when dragging outside of the window:
https://bugzilla.mozilla.org/show_bug.cgi?id=665704
https://bugzilla.mozilla.org/show_bug.cgi?id=656164
我正在尝试为此开发一种解决方法(我知道这是可能的,因为 Gmail 正在这样做),但我唯一能想到的似乎真的很老套.
I'm trying to develop a workaround for this (which I know is possible because Gmail is doing it), but the only thing I can come up with seems really hackish.
知道何时拖到窗口外的一种方法是等待 dragover
事件停止触发(因为 dragover
在拖放操作期间会不断触发).以下是我的做法:
One way of knowing when dragging outside the window has occurred it to wait for the dragover
event to stop firing (because dragover
fires constantly during a drag and drop operation). Here's how I'm doing that:
var timeout;
function dragleaveFunctionality() {
// do stuff
}
function firefoxTimeoutHack() {
clearTimeout(timeout);
timeout = setTimeout(dragleaveFunctionality, 200);
}
$(document).on('dragover', firefoxTimeoutHack);
这段代码本质上是一次又一次地创建和清除超时.除非 dragover
事件停止触发,否则不会达到 200 毫秒超时.
This code is essentially creating and clearing a timeout over and over again. The 200 millisecond timeout will not be reached unless the dragover
event stops firing.
虽然这可行,但我不喜欢为此目的使用超时的想法.感觉不对.这也意味着在dropzone"样式消失之前会有一点延迟.
While this works, I don't like the idea of using a timeout for this purpose. It feels wrong. It also means there's a slight lag before the "dropzone" styling goes away.
我的另一个想法是检测鼠标何时离开窗口,但是在拖放操作期间执行此操作的正常方法似乎不起作用.
The other idea I had was to detect when the mouse leaves the window, but the normal ways of doing that don't seem to work during drag and drop operations.
有没有人有更好的方法来做到这一点?
Does anyone out there have a better way of doing this?
更新:
这是我正在使用的代码:
Here's the code I am using:
$(function() {
var counter = 0;
$(document).on('dragenter', function(e) {
counter += 1;
console.log(counter, e.target);
});
$(document).on('dragleave', function(e) {
counter -= 1;
console.log(counter, e.target);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Open up the console and look at what number is reporting when dragging files in and out of the window. The number should always be 0 when leaving the window, but in Firefox it's not.</p>
推荐答案
我找到了解决方案.问题不在于 dragleave
事件没有触发;相反,dragenter
事件在第一次将文件拖入窗口时触发了两次(另外有时在拖动某些元素时).我最初的解决方案是使用计数器来跟踪最终的 dragenter
事件的双重触发却弄乱了计数.(为什么我不能只听你问的 dragleave
?好吧,因为 dragleave
的功能与 mouseout
非常相似,因为它不仅会在以下情况下触发离开元素,但在进入子元素时也一样.因此,当 dragleave
触发时,您的鼠标很可能仍然在原始元素的范围内.)
I've found a solution. The problem was not so much that the dragleave
event wasn't firing; rather, the dragenter
event was firing twice when first dragging a file into the window (and additionally sometimes when dragging over certain elements). My original solution was to use a counter to track when the final dragleave
event was occuring, but the double firing of dragenter
events was messing up the count. (Why couldn't I just listen for dragleave
you ask? Well, because dragleave
functions very similarly to mouseout
in that it fires not only when leaving the element but also when entering a child element. Thus, when dragleave
fires, your mouse may very well still be within the bounds of the original element.)
我想出的解决方案是跟踪哪些元素 dragenter
和 dragleave
已被触发.由于事件会向上传播到文档,因此在特定元素上侦听 dragenter
和 dragleave
不仅会捕获该元素上的事件,还会捕获其子元素上的事件.
The solution I came up with was to keep track of which elements dragenter
and dragleave
had been triggered on. Since events propagate up to the document, listening for dragenter
and dragleave
on a particular element will capture not only events on that element but also events on its children.
所以,我创建了一个 jQuery 集合 $()
来跟踪在哪些元素上触发了哪些事件.每当 dragenter 被触发时,我就将 event.target
添加到集合中,并且每当 dragleave 发生时,我就从集合中删除 event.target
.这个想法是,如果集合为空,则意味着我实际上已经离开了原始元素,因为如果我输入的是子元素,则至少一个元素(子元素)仍会在 jQuery 集合中.最后,当 drop
事件被触发时,我想将集合重置为空,以便在下一个 dragenter
事件发生时准备就绪.
So, I created a jQuery collection $()
to keep track of what events were fired on what elements. I added the event.target
to the collection whenever dragenter was fired, and I removed event.target
from the collection whenever dragleave happened. The idea was that if the collection were empty it would mean I had actually left the original element because if I were entering a child element instead, at least one element (the child) would still be in the jQuery collection. Lastly, when the drop
event is fired, I want to reset the collection to empty, so it's ready to go when the next dragenter
event occurs.
jQuery 还节省了很多额外的工作,因为它会自动进行重复检查,因此 event.target
不会被添加两次,即使 Firefox 错误地重复调用 dragenter代码>.
jQuery also saves a lot of extra work because it automatically does duplicate checking, so event.target
doesn't get added twice, even when Firefox was incorrectly double-invoking dragenter
.
唷,无论如何,这是我最终使用的代码的基本版本.如果其他人有兴趣使用它,我已将其放入一个简单的 jQuery 插件中.基本上,你在任何元素上调用 .draghover
,当第一次拖入元素时触发 draghoverstart
,一旦拖入元素就会触发 draghoverend
居然离开了.
Phew, anyway, here's a basic version of the code I ended up using. I've put it into a simple jQuery plugin if anyone else is interested in using it. Basically, you call .draghover
on any element, and draghoverstart
is triggered when first dragging into the element, and draghoverend
is triggered once the drag has actually left it.
// The plugin code
$.fn.draghover = function(options) {
return this.each(function() {
var collection = $(),
self = $(this);
self.on('dragenter', function(e) {
if (collection.length === 0) {
self.trigger('draghoverstart');
}
collection = collection.add(e.target);
});
self.on('dragleave drop', function(e) {
collection = collection.not(e.target);
if (collection.length === 0) {
self.trigger('draghoverend');
}
});
});
};
// Now that we have a plugin, we can listen for the new events
$(window).draghover().on({
'draghoverstart': function() {
console.log('A file has been dragged into the window.');
},
'draghoverend': function() {
console.log('A file has been dragged out of window.');
}
});
没有 jQuery
要在不使用 jQuery 的情况下处理此问题,您可以执行以下操作:
To handle this without jQuery you can do something like this:
// I want to handle drag leaving on the document
let count = 0
onDragEnter = (event) => {
if (event.currentTarget === document) {
count += 1
}
}
onDragLeave = (event) => {
if (event.currentTarget === document) {
count += 0
}
if (count === 0) {
// Handle drag leave.
}
}
相关文章