【问题标题】:Prevent browser from loading a drag-and-dropped file阻止浏览器加载拖放文件
【发布时间】:2011-10-09 01:16:21
【问题描述】:

我正在向我的页面添加一个 html5 拖放上传器。

将文件放入上传区域后,一切正常。

但是,如果我不小心将文件放到了上传区域之外,浏览器会像加载新页面一样加载本地文件。

如何防止这种行为?

谢谢!

【问题讨论】:

  • 只是好奇您使用什么代码来处理 html5 拖放上传。谢谢。
  • 您遇到的问题是由于缺少 e.dataTransfer() 或在 drop/dragenter/etc 上缺少 preventDefault() 引起的。事件。但如果没有代码示例,我无法判断。

标签: javascript jquery html drag-and-drop


【解决方案1】:

您可以在窗口中添加一个事件侦听器,在所有拖放事件上调用preventDefault()
示例:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);

【讨论】:

  • dragover 是我错过的那块。
  • 我确认需要dragoverdrop 处理程序来防止浏览器加载删除的文件。 (Chrome 最新 2015/08/03)。该解决方案也适用于 FF latest。
  • 这非常有效,我可以确认它可以与配置为接受放置事件的页面元素结合使用,例如来自拖放文件上传脚本(如 resumable.js)的那些.在用户不小心将他们想要上传的文件放到实际文件上传放置区之外,然后想知道为什么他们现在看到直接在浏览器窗口中呈现的同一个文件的情况下,防止默认浏览器行为很有用(假设像图片或视频这样的兼容文件类型被丢弃),而不是看到他们的文件上传的预期行为。
  • 注意:这也禁止将文件拖动到<input type="file" />。需要检查e.target是否为文件输入,让此类事件通过。
  • 什么?为什么要窗口拖动加载文件?这没有任何意义......
【解决方案2】:

试试这个:

document.body.addEventListener('drop', function(e) {
    e.preventDefault();
}, false);

【讨论】:

    【解决方案3】:

    默认情况下阻止所有拖放操作可能不是您想要的。至少在某些浏览器中,可以检查拖动源是否为外部文件。我在StackOverflow answer 中包含了一个检查拖动源是否为外部文件的函数。

    修改数字飞机的答案,你可以这样做:

    function isDragSourceExternalFile() {
         // Defined here: 
         // https://stackoverflow.com/a/32044172/395461
    }
    
    window.addEventListener("dragover",function(e){
        e = e || event;
        var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
        if (IsFile) e.preventDefault();
    },false);
    window.addEventListener("drop",function(e){
        e = e || event;
        var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
        if (IsFile) e.preventDefault();
    },false);
    

    【讨论】:

    • e || event; 的意义何在? event 在哪里定义?没关系。看起来它是 IE 中的全局对象?我找到了这句话,"In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object."here
    【解决方案4】:

    要允许仅在某些元素上拖放,您可以执行以下操作:

    window.addEventListener("dragover",function(e){
      e = e || event;
      console.log(e);
      if (e.target.tagName != "INPUT") { // check which element is our target
        e.preventDefault();
      }
    },false);
    window.addEventListener("drop",function(e){
      e = e || event;
      console.log(e);
      if (e.target.tagName != "INPUT") {  // check which element is our target
        e.preventDefault();
      }  
    },false);
    

    【讨论】:

    • 非常适合我,但我也会添加对 type=file 的检查,否则您仍然可以拖动到文本输入
    【解决方案5】:

    经过一番折腾,我发现这是最稳定的解决方案:

    var dropzoneId = "dropzone";
    
    window.addEventListener("dragenter", function(e) {
      if (e.target.id != dropzoneId) {
        e.preventDefault();
        e.dataTransfer.effectAllowed = "none";
        e.dataTransfer.dropEffect = "none";
      }
    }, false);
    
    window.addEventListener("dragover", function(e) {
      if (e.target.id != dropzoneId) {
        e.preventDefault();
        e.dataTransfer.effectAllowed = "none";
        e.dataTransfer.dropEffect = "none";
      }
    });
    
    window.addEventListener("drop", function(e) {
      if (e.target.id != dropzoneId) {
        e.preventDefault();
        e.dataTransfer.effectAllowed = "none";
        e.dataTransfer.dropEffect = "none";
      }
    });
    <div id="dropzone">...</div>

    在窗口上无条件设置 effectAllowdropEffect 会导致我的拖放区不再接受任何 d-n-d,无论属性是否设置为新。

    【讨论】:

    • e.dataTransfer() 是使这项工作发挥作用的关键部分,“接受的答案”没有提及。
    • 您可以从拖放区的事件处理程序中调用event.stopPropagation(),而不是检查e.target.id。另外,这里没有必要像@HoldOffHunger 提到的那样设置effectedAlled
    【解决方案6】:

    对于 jQuery,正确答案是:

    $(document).on({
        dragover: function() {
            return false;
        },
        drop: function() {
            return false;
        }
    });
    

    这里return false 将表现为event.preventDefault()event.stopPropagation()

    【讨论】:

      【解决方案7】:

      要建立在其他几个答案中概述的“检查目标”方法的基础上,这里有一个更通用/功能更强大的方法:

      function preventDefaultExcept(predicates) {
        return function (e) {
          var passEvery = predicates.every(function (predicate) { return predicate(e); })
          if (!passEvery) {
            e.preventDefault();
          }
        };
      }
      

      调用如下:

      function isDropzone(e) { return e.target.id === 'dropzone'; }
      function isntParagraph(e) { return e.target.tagName !== 'p'; }
      
      window.addEventListener(
        'dragover',
        preventDefaultExcept([isDropzone, isntParagraph])
      );
      window.addEventListener(
        'drop',
        preventDefaultExcept([isDropzone])
      );
      

      【讨论】:

      • 另外,可以在这里添加一些 ES6:function preventDefaultExcept(...predicates){}。然后像preventDefaultExcept(isDropzone, isntParagraph) 一样使用它
      【解决方案8】:

      我有一个填充页面宽度和高度的 HTML object (embed)。 @digital-plane 的答案适用于普通网页,但如果用户掉到嵌入式对象上则不行。所以我需要一个不同的解决方案。

      如果我们切换到使用event capture phase,我们可以在嵌入对象接收到事件之前获取事件(注意事件监听器调用结束时的true值):

      // document.body or window
      document.body.addEventListener("dragover", function(e){
        e = e || event;
        e.preventDefault();
        console.log("over true");
      }, true);
      
      document.body.addEventListener("drop", function(e){
        e = e || event;
        e.preventDefault();
        console.log("drop true");
      }, true);
      

      使用以下代码(基于@digital-plane 的回答)页面成为拖动目标,它防止对象嵌入捕获事件然后加载我们的图像:

      document.body.addEventListener("dragover", function(e){
        e = e || event;
        e.preventDefault();
        console.log("over true");
      }, true);
      
      document.body.addEventListener("drop",function(e){
        e = e || event;
        e.preventDefault();
        console.log("Drop true");
      
        // begin loading image data to pass to our embed
        var droppedFiles = e.dataTransfer.files;
        var fileReaders = {};
        var files = {};
        var reader;
      
        for (var i = 0; i < droppedFiles.length; i++) {
          files[i] = droppedFiles[i]; // bc file is ref is overwritten
          console.log("File: " + files[i].name + " " + files[i].size);
          reader = new FileReader();
          reader.file = files[i]; // bc loadend event has no file ref
      
          reader.addEventListener("loadend", function (ev, loadedFile) {
            var fileObject = {};
            var currentReader = ev.target;
      
            loadedFile = currentReader.file;
            console.log("File loaded:" + loadedFile.name);
            fileObject.dataURI = currentReader.result;
            fileObject.name = loadedFile.name;
            fileObject.type = loadedFile.type;
            // call function on embed and pass file object
          });
      
          reader.readAsDataURL(files[i]);
        }
      
      }, true);
      

      在 Mac 上的 Firefox 上测试。

      【讨论】:

        【解决方案9】:

        我正在为多个上传区域使用类选择器,所以我的解决方案采用了这种不太纯粹的形式

        基于 Axel Amthor 的回答,依赖于 jQuery(别名为 $)

        _stopBrowserFromOpeningDragAndDropPDFFiles = function () {
        
                _preventDND = function(e) {
                    if (!$(e.target).is($(_uploadBoxSelector))) {
                        e.preventDefault();
                        e.dataTransfer.effectAllowed = 'none';
                        e.dataTransfer.dropEffect = 'none';
                    }
                };
        
                window.addEventListener('dragenter', function (e) {
                    _preventDND(e);
                }, false);
        
                window.addEventListener('dragover', function (e) {
                    _preventDND(e);
                });
        
                window.addEventListener('drop', function (e) {
                    _preventDND(e);
                });
            },
        

        【讨论】:

          【解决方案10】:

          注意:虽然 OP 没有要求 Angular 解决方案,但我还是来这里寻找的。因此,如果您使用 Angular,这是为了分享我发现的可行解决方案。

          根据我的经验,当您向页面添加文件拖放功能时,首先会出现此问题。因此,我的观点是,添加这个的组件也应该负责防止掉出放置区。

          在我的解决方案中,拖放区是一个带有类的输入,但任何明确的选择器都可以。

          import { Component, HostListener } from '@angular/core';
          //...
          
          @Component({
            template: `
              <form>
                <!-- ... -->
                <input type="file" class="dropzone" />
              </form>
            `
          })
          export class MyComponentWithDropTarget {
          
            //...
          
            @HostListener('document:dragover', ['$event'])
            @HostListener('drop', ['$event'])
            onDragDropFileVerifyZone(event) {
              if (event.target.matches('input.dropzone')) {
                // In drop zone. I don't want listeners later in event-chain to meddle in here
                event.stopPropagation();
              } else {
                // Outside of drop zone! Prevent default action, and do not show copy/move icon
                event.preventDefault();
                event.dataTransfer.effectAllowed = 'none';
                event.dataTransfer.dropEffect = 'none';
              }
            }
          }
          

          监听器在组件创建/销毁时自动添加/删除,同一页面上使用相同策略的其他组件由于stopPropagation()不会相互干扰。

          【讨论】:

          • 这就像一个魅力!浏览器甚至通过添加禁止图标来更改鼠标光标,非常棒!
          • "Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone." 好点子!
          猜你喜欢
          • 2018-03-30
          • 2014-03-06
          • 2019-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-27
          • 1970-01-01
          相关资源
          最近更新 更多