在窗口外拖动时如何检测Firefox中的dragleave事件
Posted
技术标签:
【中文标题】在窗口外拖动时如何检测Firefox中的dragleave事件【英文标题】:How to detect the dragleave event in Firefox when dragging outside the window 【发布时间】:2012-05-02 11:28:09 【问题描述】:在窗口外拖动时,Firefox 没有正确触发 dragleave 事件:
https://bugzilla.mozilla.org/show_bug.cgi?id=665704
https://bugzilla.mozilla.org/show_bug.cgi?id=656164
我正在尝试为此开发一种解决方法(我知道这是可能的,因为 Gmail 正在这样做),但我唯一能想到的似乎真的很老套。
知道何时在窗口外拖动的一种方法是等待dragover
事件停止触发(因为dragover
在拖放操作期间不断触发)。以下是我的做法:
var timeout;
function dragleaveFunctionality()
// do stuff
function firefoxTimeoutHack()
clearTimeout(timeout);
timeout = setTimeout(dragleaveFunctionality, 200);
$(document).on('dragover', firefoxTimeoutHack);
这段代码本质上是一次又一次地创建和清除超时。除非dragover
事件停止触发,否则不会达到 200 毫秒超时。
虽然这可行,但我不喜欢为此目的使用超时的想法。感觉不对。这也意味着在“dropzone”样式消失之前会有一点延迟。
我的另一个想法是检测鼠标何时离开窗口,但执行此操作的正常方法在拖放操作期间似乎不起作用。
有没有人有更好的方法来做到这一点?
更新:
这是我正在使用的代码:
$(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>
【问题讨论】:
问题在 Firefox 11 中似乎已修复 - 您的目标是什么版本? 我仍然在 Firefox 11 上看到它,根据错误报告,它尚未修复。我会看看我是否可以制作一个演示链接。 请 - 这可能有助于查明问题。我制作了一个简单的页面并绑定到文档上的拖动,当我拖动离开文档时它为我触发。我还发现 this... 有趣的是,某些绑定了事件的事件会影响其他事件的触发 我看到的是处理程序在进入时触发两次,在离开时触发一次。我不知道我是否有同样的问题。火狐 11.0。 如果您在安全模式下启动 Firefox(无插件)会怎样?我刚刚按原样尝试了您的演示页面,进入时得到 1,退出时得到 0。 【参考方案1】:我找到了解决方案。问题不在于dragleave
事件没有触发;相反,dragenter
事件在第一次将文件拖入窗口时触发了两次(另外有时在拖动某些元素时)。我最初的解决方案是使用计数器来跟踪最终的dragleave
事件何时发生,但dragenter
事件的双重触发会打乱计数。 (为什么我不能只听dragleave
你问?好吧,因为dragleave
的功能与mouseout
非常相似,因为它不仅在离开元素时触发,而且在进入子元素时触发。因此,当@ 987654329@ 触发,您的鼠标很可能仍在原始元素的范围内。)
我想出的解决方案是跟踪哪些元素 dragenter
和 dragleave
已被触发。由于事件会传播到文档,因此在特定元素上侦听 dragenter
和 dragleave
不仅会捕获该元素上的事件,还会捕获其子元素上的事件。
所以,我创建了一个 jQuery 集合 $()
来跟踪在哪些元素上触发了哪些事件。每当 Dragenter 被触发时,我都会将 event.target
添加到集合中,并且每当 Dragleave 发生时,我都会从集合中删除 event.target
。这个想法是,如果集合为空,则意味着我实际上已经离开了原始元素,因为如果我输入的是子元素,那么至少一个元素(子元素)仍会在 jQuery 集合中。最后,当drop
事件被触发时,我想将集合重置为空,以便在下一个dragenter
事件发生时准备就绪。
jQuery 还节省了大量额外的工作,因为它会自动进行重复检查,因此 event.target
不会被添加两次,即使 Firefox 错误地重复调用 dragenter
。
唷,无论如何,这是我最终使用的代码的基本版本。如果其他人有兴趣使用它,我已将其放入一个简单的 jQuery 插件中。基本上,你在任何元素上调用.draghover
,draghoverstart
在第一次拖入元素时触发,draghoverend
在拖拽实际离开时触发。
// 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 的情况下处理此问题,您可以执行以下操作:
// 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.
【讨论】:
我将drop
添加到dragleave
处理程序,因为这可能也算作draghoverend
。 cmets 要点:gist.github.com/3794126
@meleyal,没关系。 FWIW,我最初没有包含它,因为 drop 事件在所有浏览器中始终发生,我觉得没有必要对其进行规范化。
我遇到了集合中的最后一个元素没有被删除的情况。这是因为 drop
是最后一个触发的事件,所以 dragleave
不会被调用。
这在 Chrome 和 Firefox 中对我来说似乎有点问题。似乎是页面上的一些元素我尚未确定触发休假。但是与普通的dragleave
实现相比,它确实使问题更容易忍受。如果我能解决它会报告。
这种方法仍然有效,但解决方案本身需要稍作改动。具体来说,所有对 e.target 的引用都需要改为 e.originalEvent.target。这解决了 #text 元素(p 标签的内容)在 firefox 中触发 dragenter/dragleave 的问题。 jQuery 事件将这些对 的引用标准化,这会导致此处建议的制表方案出现问题。通过此更改,dragend/dragleave 事件似乎按预期工作。
【参考方案2】:根据您希望完成的任务,您可以通过使用仅在 Firefox 中可用的 :-moz-drag-over
伪类来解决此问题,它可以让您对拖动到元素上的文件做出反应。
看看这个简单的演示http://codepen.io/ryanseddon/pen/Ccsua
.dragover
background: red;
width: 500px;
height: 300px;
.dragover:-moz-drag-over
background: green;
【讨论】:
【参考方案3】:受@PhilipWalton 的代码启发,我简化了 jQuery 插件代码。
$.fn.draghover = function(fnIn, fnOut)
return this.each(function()
var n = 0;
$(this).on('dragenter', function(e)
(++n, n==1) && fnIn && fnIn.call(this, e);
).on('dragleave drop', function(e)
(--n, n==0) && fnOut && fnOut.call(this, e);
);
);
;
现在你可以像jquery hover方法一样使用jquery插件了:
// Testing code 1
$(window).draghover(function()
console.log('into window');
, function()
console.log('out of window');
);
// Testing code 2
$('#d1').draghover(function()
console.log('into #d1');
, function()
console.log('out of #d1');
);
【讨论】:
【参考方案4】:唯一对我有用并花了我一些时间的解决方案希望这对某人有所帮助!
注意克隆时需要对事件和数据进行深度克隆:
html:
<div class="dropbox"><p>Child element still works!</p></div>
<div class="dropbox"></div>
<div class="dropbox"></div>
jQuery
$('.dropbox').each(function(idx, el)
$(this).data("counter" , 0);
);
$('.dropbox').clone(true,true).appendTo($('body');
$('dropbox').on(
dragenter : function(e)
$(this).data().counter++;
<!-- YOUR CODE HERE -->
,
dragleave: function(e)
$(this).data().counter--;
if($(this).data().counter === 0)
<!-- THEN RUN YOUR CODE HERE -->
);
【讨论】:
【参考方案5】:addEvent(document, "mouseout", function(e)
e = e ? e : window.event;
var from = e.relatedTarget || e.toElement;
if (!from || from.nodeName == "HTML")
// stop your drag event here
// for now we can just use an alert
alert("left window");
);
这是从How can I detect when the mouse leaves the window? 复制的。 addEvent 只是跨浏览器 addEventListener。
【讨论】:
不,这不起作用,不应该被赞成。正如我在问题中所说,典型的鼠标事件在拖放操作期间不会触发。 没有看到关于不触发的典型事件以上是关于在窗口外拖动时如何检测Firefox中的dragleave事件的主要内容,如果未能解决你的问题,请参考以下文章