首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Javascript -双重文件拖拽事件触发

Javascript -双重文件拖拽事件触发
EN

Stack Overflow用户
提问于 2013-01-18 12:16:28
回答 4查看 3.9K关注 0票数 4

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

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

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

代码语言:javascript
复制
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"));
        }
    }); 

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-01-18 13:13:51

无名氏(大部分)是正确的。简单地说:当鼠标移动到拖放目标中元素的边缘时,您将获得光标下元素的dropenter和之前光标下元素的dropleave。这绝对会发生在任何后代身上。

您不能检查与dragleave关联的元素,因为如果您将鼠标从拖放目标移动到子元素上,您将获得该子元素的dropenter,然后是目标的dropleave!这有点可笑,我根本看不出这是一个有用的设计。

这是我不久前想出的一个糟糕的基于jQuery的解决方案。

代码语言:javascript
复制
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将按该顺序为目标元素排队。
  • dragenterdragleave一起排队。

这就是发生的事情。

  • dragenter事件中,我设置了一些共享变量来指示拖动移动尚未完成解析。
  • 我使用延迟为零的setTimeout立即将该变量改回。
  • 但是!因为这两个事件在同一时间排队,所以在两个事件完成解析之前,浏览器不会运行任何调度函数。因此,接下来发生的事情是dragleave的事件处理程序。
  • 如果dragleave发现它与同一目标元素上的dragenter配对,这意味着鼠标一定是从某个后代移动到了另一个后代。否则,鼠标实际上离开了目标element.
  • Then,setTimeout最终在几秒钟后解析为零,在另一个事件出现之前将该变量重新设置为零。

我想不出更简单的方法了。

票数 13
EN

Stack Overflow用户

发布于 2013-01-18 13:00:57

您正在为dragoverdragleavedropBODY HTMLElement上添加侦听程序。

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

其次,由于您没有停止DIV上的气泡事件(未设置侦听器),因此在DIV上触发的拖拽将阻塞到BODY

如果我继续:

鼠标进入身体(处于拖拽状态)

--> fire在(body)上拖动

鼠标进入身体中的DIV

--> fire拖动离开(主体)

--> fire drag over (of DIV) --> event bubling --> fire drag over (of BODY)

mouseovermouseout也存在类似的问题,可以使用mouseentermouseleave修复。

也许您可以使用dragenter事件类型尝试相同的代码。如果它不工作,你可以检查event.target是否是BODY。此测试有助于跳过不需要的拖动事件。

祝好运

票数 0
EN

Stack Overflow用户

发布于 2013-11-12 04:53:26

代码语言:javascript
复制
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;
    }
}); 
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14392293

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档