Javascript - 双重文件Dragover事件射击

Ian*_*Ian 4 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"));
        }
    }); 
Run Code Online (Sandbox Code Playgroud)

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

Eev*_*vee 12

这个(大部分)是正确的.简单地说:当鼠标移动到放置目标内的元素边缘时,您将获得光标下的dropenter元素和前面光标下的元素.绝对任何后代都会发生这种情况.dropleave

您无法检查与之关联的元素dragleave,因为如果您将鼠标放置目标移动子元素上,您将为dropenter孩子获得一个,然后dropleave为目标获得一个!这有点荒谬,我根本不知道这是多么有用的设计.

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

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

诀窍依赖于两个事实:

  • 当您从孙子将鼠标移动到一个孩子,都dragenterdragleave将排队等候目标元素- 的顺序.
  • dragenterdragleave排队在一起.

所以这就是发生的事情.

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

我想不出更简单的方法.