【问题标题】:How to detect the dragleave event in Firefox when dragging outside the window在窗口外拖动时如何检测Firefox中的dragleave事件
【发布时间】: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。

标签: javascript jquery html drag-and-drop


【解决方案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。

        【讨论】:

        • 不,这不起作用,不应该被赞成。正如我在问题中所说,典型的鼠标事件在拖放操作期间不会触发。
        • 没有看到关于不触发的典型事件
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 2012-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-19
        • 1970-01-01
        相关资源
        最近更新 更多