我正在尝试创建文件拖放处理程序(将文件拖到浏览器窗口中,用于上传)。
出于某种原因,当我将拖放监听器绑定(bind)到 $("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"));
}
});
测试:将文件拖到橙色区域,您会看到事件连续触发多次。
最佳答案
匿名者(大部分)是正确的。简而言之:当鼠标移过放置目标内元素的边缘时,您会得到光标下元素的dropenter
> 和之前光标下的元素的 dropleave
。这绝对发生在任何后代身上。
您无法检查与 dragleave
关联的元素,因为如果您将鼠标从您的放置目标移动到一个子元素,您将为 child 获得一个 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);
});
这个技巧依赖于两个事实:
- 当您将鼠标从孙元素移动到子元素时,
dragenter
和dragleave
都将排队等待目标元素 —按此顺序. dragenter
和dragleave
一起排队。
这就是发生的事情。
- 在
dragenter
事件中,我设置了一些共享变量来指示拖动运动尚未完成解析。 - 我使用延迟为零的
setTimeout
立即将该变量改回。 - 但是!因为这两个事件在同一时间排队,所以在两个事件完成解析之前,浏览器不会运行任何计划的功能。所以接下来发生的事情是
dragleave
的事件处理程序。 - 如果
dragleave
发现它与同一目标元素上的dragenter
配对,则意味着鼠标必须已从某个后代移动给其他一些后代。否则,鼠标实际上离开了目标元素。 - 然后
setTimeout
最终在 0 秒后解析,在另一个事件出现之前设置回变量。
我想不出更简单的方法。
关于Javascript - 双文件拖动事件触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14392293/