在窗口外拖动时如何检测Firefox中的dragleave事件

在窗口外拖动时,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); 

这个代码本质上是一遍又一遍地创build和清除一个超时。 除非dragover事件停止发射,否则将不会达到200毫秒超时。

虽然这有效,但我不喜欢为此使用超时的想法。 感觉不对 这也意味着在“dropzone”造型消失之前还有一点点滞后。

我的另一个想法是检测鼠标何时离开窗口,但是正常的做法似乎在拖放操作中不起作用。

有没有人有更好的方式做到这一点?

更新:

这里是我使用的代码:

 <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Drag and Drop Issue</title> <script src="http://code.jquery.com/jquery.js"></script> </head> <body> 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. <script type="text/javascript"> $(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> </body> </html> 

我find了一个解决scheme。 问题并不在于dragleave事件没有解决。 相反,当第一次将文件拖入窗口时, dragenter事件会被触发两次(另外有时还会拖动某些元素)。 我最初的解决scheme是使用一个计数器来追踪最终的dragleave事件发生的时间,但是dragenter事件的双重发射正在dragleave计数。 (为什么我不能只听到你问的dragleave ?嗯,因为dragleavefunction非常类似于mouseout ,因为它不仅在离开元素时,而且在进入子元素时dragleave触发,因此,当dragleave触发时,鼠标可能会非常仍然在原始元素的范围内。)

我提出的解决scheme是跟踪dragenterdragleave被触发的元素。 由于事件传播到文件中,倾听dragenterdragleave在一个特定的元素将不仅捕获该元素上的事件,而且捕获其子项上的事件。

所以,我创build了一个jQuery集合$()来跟踪什么元素被触发了什么事件。 每当dragenter被激发时,我都将event.target到集合中,每当dragleave发生时,我都会从集合中删除event.target 。 这个想法是,如果集合是空的,这意味着我实际上离开了原始元素,因为如果我input了一个子元素,至less有一个元素(子元素)仍然在jQuery集合中。 最后,当drop事件被触发时,我想把collection重置为空,所以当下一个dragenter事件发生的时候就准备好了。

jQuery还节省了大量额外的工作,因为它自动执行复制检查,所以event.target不会被添加两次,即使Firefox不正确地双重调用dragenter

唷,无论如何,这是我最终使用的代码的基本版本。 如果其他人有兴趣使用它,我把它放到一个简单的jQuery插件中。 基本上,你.draghover在任何元素上调用.draghover ,并且在第一次拖拽元素时触发draghoverend ,一旦拖拽实际上离开它,触发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.'); } }); 

取决于你想完成什么,你可以通过使用:-moz-drag-over伪类来解决这个问题,它只在Firefox中可用,它允许你对被拖拽到一个元素上的文件作出反应。

看看这个简单的演示http://codepen.io/ryanseddon/pen/Ccsua

 .dragover { background: red; width: 500px; height: 300px; } .dragover:-moz-drag-over { background: green; } 
 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"); } }); 

这是从我怎样才能检测到鼠标离开窗口? 。 addEvent只是交叉浏览器的addEventListener。

受@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'); }); 

只有解决scheme,为我工作,带了我几个希望这可以帮助别人!

注意克隆时需要使用事件和数据进行深层克隆:

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 --> } });