4

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

出于某种原因,当我将拖放侦听器绑定到$("body")而不是绑定到$("div")主体中的 a 时,事件会连续触发多次,有时甚至是不间断的(看似循环)。这可能是什么原因造成的?

这是代码的精简版本: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

4 回答 4

13

匿名是(大部分)正确的。简而言之:当鼠标移动到放置目标内元素的边缘时,您将获得光标下dropenter元素的 a 和先前光标下的元素的a 。这绝对发生在任何后代身上。dropleave

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

这是我前段时间想出的一个糟糕的基于 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);
});

诀窍依赖于两个事实:

  • 当您将鼠标从孙子元素移到子元素时,两者都dragenterdragleave排队等待目标元素——<em>按此顺序。
  • dragenter一起dragleave排队。_

所以这就是发生的事情。

  • 在这种情况dragenter下,我设置了一些共享变量来指示拖动运动尚未完成解决。
  • 我使用setTimeout零延迟来立即更改该变量。
  • 但!因为这两个事件在同一时间排队,所以在这两个事件完成解析之前,浏览器不会运行任何预定的功能。所以接下来发生的是dragleave的事件处理程序。
  • 如果dragleave看到它与dragenter同一目标元素上的 a 配对,则意味着鼠标必须从某个后代移动到另一个后代。否则,鼠标实际上正在离开目标元素。
  • 然后setTimeoutfinally 在零秒后解决,在另一个事件发生之前设置回变量。

我想不出更简单的方法。

于 2013-01-18T05:13:50.580 回答
0

您正在为和的BODYHTMLElement添加一个侦听dragover器。dragleavedrop

当您继续在 DIV 上拖动时dragleave,会触发一个,因为鼠标不再拖动到 BODY 上,而是在 DIV 上。

其次,由于您没有停止DIV(未设置侦听器)上的气泡事件,因此DIVBODY.

如果我恢复:

鼠标进入正文(在dragover中)

-->火拖过(身体)

鼠标进入体内的DIV

-->火拖离开(BODY)

-->火拖 (DIV) -->事件冒泡-->火拖 (BODY)

和也有类似的问题,可以通过使用 和 来mouseover解决。mouseoutmouseentermouseleave

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

祝你好运

于 2013-01-18T05:00:56.537 回答
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;
    }
}); 
于 2013-11-11T20:53:26.070 回答
0

或者你可以stop();用来停止动画的建立

于 2014-06-10T20:22:22.317 回答