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

Phi*_*ton 36 javascript jquery html5 drag-and-drop

在窗口外拖动时,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);
Run Code Online (Sandbox Code Playgroud)

此代码实际上是一次又一次地创建和清除超时.除非dragover事件停止触发,否则将无法达到200毫秒的超时.

虽然这有效,但我不喜欢为此目的使用超时的想法.感觉不对.这也意味着在"掉落区"造型消失之前会有一点滞后.

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

有没有人有更好的方法这样做?

更新:

这是我正在使用的代码:

<!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>
Run Code Online (Sandbox Code Playgroud)

Phi*_*ton 57

我找到了解决方案.问题不在于dragleave事件没有发生; 相反,dragenter当第一次将文件拖入窗口时(另外有时拖动某些元素时),事件会触发两次.我最初的解决方案是使用计数器跟踪最终dragleave事件发生的时间,但是dragenter事件的双重触发使计数变得混乱.(为什么我不能只听dragleave你问?好吧,因为dragleave功能非常类似于mouseout它不仅在离开元素时dragleave发射,而且在进入子元素时发射.因此,当发生火灾时,你的鼠标可能仍然在原始元素的界限.)

我想出了解决的办法是跟踪哪些元素dragenter,并dragleave已被触发.由于事件传播到文档,因此侦听dragenterdragleave访问特定元素不仅会捕获该元素上的事件,还会捕获其子元素上的事件.

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

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

Phew,无论如何,这是我最终使用的代码的基本版本.如果其他人有兴趣使用它,我已将它放入一个简单的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.');
  }
});
Run Code Online (Sandbox Code Playgroud)

  • 这种方法仍然有效,但解决方案本身需要稍作改动.具体而言,所有对e.target的引用都需要为e.originalEvent.target.这解决了#text元素(ap标签的内容)在firefox中触发dragenter/dragleave的问题.jQuery事件将这些引用规范化为<p>,这会导致此处建议的制表方案出现问题.通过此更改,dragend/dragleave事件似乎按预期工作. (3认同)