36

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>

4

5 回答 5

58

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

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

因此,我创建了一个 jQuery 集合$()来跟踪在哪些元素上触发了哪些事件。event.target每当 dragenter 被触发时,我就将其添加到集合中,并且event.target每当 dragleave 发生时,我就从集合中删除。这个想法是,如果集合为空,则意味着我实际上已经离开了原始元素,因为如果我输入的是子元素,那么至少一个元素(子元素)仍会在 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.
  }
}
于 2012-04-25T07:00:57.410 回答
3

根据您希望完成的任务,您可以使用:-moz-drag-over仅在 Firefox 中可用的伪类来解决此问题,该伪类可让您对拖动到元素上的文件做出反应。

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

.dragover {
    background: red;
    width: 500px;
    height: 300px;
}
.dragover:-moz-drag-over {
    background: green;
}
于 2013-04-02T03:23:04.880 回答
0

受@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 插件,如 jquery hover 方法:

// 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');
});
于 2015-04-22T20:58:42.307 回答
0

唯一对我有用并花了我一些时间的解决方案希望这对某人有所帮助!

克隆时请注意,您需要使用事件和数据进行深度克隆:

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 -->
    }
});
于 2015-07-27T12:44:43.150 回答
-1
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。

于 2012-04-23T00:34:37.990 回答