在窗口外拖动时如何检测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@ 触发,您的鼠标很可能仍在原始元素的范围内。)

我想出的解决方案是跟踪哪些元素 dragenterdragleave 已被触发。由于事件会传播到文档,因此在特定元素上侦听 dragenterdragleave 不仅会捕获该元素上的事件,还会捕获其子元素上的事件。

所以,我创建了一个 jQuery 集合 $() 来跟踪在哪些元素上触发了哪些事件。每当 Dragenter 被触发时,我都会将 event.target 添加到集合中,并且每当 Dragleave 发生时,我都会从集合中删除 event.target。这个想法是,如果集合为空,则意味着我实际上已经离开了原始元素,因为如果我输入的是子元素,那么至少一个元素(子元素)仍会在 jQuery 集合中。最后,当drop 事件被触发时,我想将集合重置为空,以便在下一个dragenter 事件发生时准备就绪。

jQuery 还节省了大量额外的工作,因为它会自动进行重复检查,因此 event.target 不会被添加两次,即使 Firefox 错误地重复调用 dragenter

唷,无论如何,这是我最终使用的代码的基本版本。如果其他人有兴趣使用它,我已将其放入一个简单的 jQuery 插件中。基本上,你在任何元素上调用.draghoverdraghoverstart在第一次拖入元素时触发,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事件的主要内容,如果未能解决你的问题,请参考以下文章

如何像 Gmail 一样检测进入和离开窗口的 HTML5 拖动事件?

在Firefox中拖动鼠标时如何防止文本选择?

IE中的窗口拖动问题

如何检测用户是不是拖动 PIP 窗口(向下拖动以关闭)?

iOS检测到屏幕左边缘外的拖动

在 Firefox 插件弹出窗口中禁用任何对象的拖动