Javascript - 双重文件Dragover事件射击

时间:2013-01-18 04:16:28

标签: javascript jquery drag-and-drop

我尝试创建文件拖放处理程序(将文件拖到浏览器窗口中,用于上传)。

出于某种原因,当我将拖放监听器绑定到$("body")而不是绑定到正文中的$("div")时,事件连续多次触发,有时甚至不停(看似循环) 。可能导致这种情况的原因是什么?

以下是代码的精简版:http://jsfiddle.net/WxMwK/9/

var over = false;

$("body")
    .on("dragover", function(e){
        e.preventDefault();
        if (! over) {
            over = true;
            $("ul").append($("<li/>").text("dragover"));    
        }
    })
    .on("dragleave", function(e){
        e.preventDefault();
        if (over) {
            over = false;
            $("ul").append($("<li/>").text("dragleave"));
        }
    })
    .on("drop", function(e){
        e.preventDefault();
        if (over) {
            over = false;
            $("ul").append($("<li/>").text("drop"));
        }
    }); 

要测试:将文件拖到橙色区域,您会看到连续多次触发事件。

4 个答案:

答案 0 :(得分:12)

anon(大多数)是正确的。简单地说:当鼠标在放置目标内的元素的边缘上移动时,你会得到{{1>}光标下的元素dropenter表示光标之前的元素。绝对任何后代都会发生这种情况。

您无法检查与dropleave相关联的元素,因为如果您将鼠标移动到一个子元素,您将会为孩子获得dragleave,然后为目标获得dropenter!这有点荒谬,我根本看不出这是一个有用的设计。

这是我前段时间提出的一个基于jQuery的糟糕解决方案。

dropleave

这个伎俩依赖于两个事实:

  • 当您将鼠标从孙子移动到孩子时,var $drop_target = $(document.body); var within_enter = false; $drop_target.bind('dragenter', function(evt) { // Default behavior is to deny a drop, so this will allow it evt.preventDefault(); within_enter = true; setTimeout(function() { within_enter = false; }, 0); // This is the part that makes the drop area light up $(this).addClass('js-dropzone'); }); $drop_target.bind('dragover', function(evt) { // Same as above evt.preventDefault(); }); $drop_target.bind('dragleave', function(evt) { if (! within_enter) { // And this makes it un-light-up :) $(this).removeClass('js-dropzone'); } within_enter = false; }); // Handle the actual drop effect $drop_target.bind('drop', function(evt) { // Be sure to reset your state down here $(this).removeClass('js-dropzone'); within_enter = false; evt.preventDefault(); do_whatever(evt.originalEvent.dataTransfer.files); }); dragenter将按顺序排列目标元素
  • dragleavedragenter一起排队

所以这就是发生的事情。

  • dragleave事件中,我设置了一些共享变量来指示拖动动作尚未完成解析。
  • 我使用延迟为零的dragenter来立即更改该变量。
  • 但是!由于这两个事件在同一时间排队,因此在两个事件完成解析之前,浏览器不会运行任何计划的功能。接下来发生的事情是setTimeout的事件处理程序。
  • 如果dragleave发现它与同一目标元素上的dragleave配对,则表示鼠标必须已从某个后代移动到其他后代。否则,鼠标实际上是离开目标元素。
  • 然后dragenter最终在零秒后解析,在另一个事件发生之前设置回变量。

我想不出更简单的方法。

答案 1 :(得分:0)

您正在为BODYdragoverdragleave的{​​{1}} HTMLElement添加听众。

当你继续拖动DIV时,有一个drop被触发,因为鼠标不再拖过BODY,而是越过DIV。

其次,由于您没有停止dragleave上的气泡事件(未设置任何侦听器),因此DIV上的诈骗者正在向DIV发出警告。

如果我恢复:

鼠标进入身体(在dragover中)

- &GT; 火力拖过(身体)

鼠标在体内输入DIV

- &GT; 火力拖动(BODY)

- &GT; 火力拖拽(DIV) - &gt; 事件冒泡 - &gt; 火力拖累(BODY)

BODYmouseover存在类似问题,使用mouseoutmouseenter修复了此问题。

您可以使用mouseleave事件类型尝试相同的代码。如果它不起作用,您可以检查dragenter是否为event.target。此测试可以帮助跳过不需要的拖动事件。

祝你好运

答案 2 :(得分:0)

var over = false; 

$("body")
.on("dragover", function(e){
    e.preventDefault();
    if (! over) {
        over = true;
        $("ul").append($("<li/>").text("dragover"));    
    }
})
.on("dragleave", function(e){
    e.preventDefault();
    if (over) {
        over = false;
        $("ul").append($("<li/>").text("dragleave"));
    }
})
.on("drop", function(e){
    e.preventDefault();
    if (over) {
        over = false;
    }
}); 

答案 3 :(得分:0)

或者您可以使用stop();来停止动画构建