防止浏览器加载拖放文件


194

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

将文件放到上载区域后,一切正常。

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

如何防止这种行为?

谢谢!


2
只是好奇您使用什么代码来处理html5拖放上传。谢谢。
robertwbradford 2011年

您遇到的问题是由于在drop / dragenter / etc上缺少e.dataTransfer()或缺少preventDefault()而引起的。事件。但是,如果没有代码示例,我将无法分辨。
HoldOffHunger

Answers:


312

您可以将事件侦听器添加到调用preventDefault()所有拖放事件的窗口。
例:

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

44
套头衫是我所缺少的那一块。
cgatian

11
我确认同时需要dragoverdrop处理程序,以防止浏览器加载放置的文件。(Chrome最新2015/08/03)。该解决方案也适用于FF最新版本。
Offirmo

4
这非常有效,我可以确认它可以与配置为接受放置事件的页面元素结合使用,例如来自诸如resumable.js的拖放文件上传脚本的事件。如果用户不小心将要上传的文件拖放到实际文件上传放置区域之外,然后想知道为什么现在他们直接在浏览器窗口中看到相同的文件,则可以防止默认的浏览器行为,这非常有用(假设删除了兼容的文件类型(例如图像或视频),而不是看到其文件上传的预期行为。
bluebinary

15
注意:这也会禁用将文件拖到上的功能<input type="file" />。有必要检查是否e.target输入了文件并让此类事件通过。
塞巴斯蒂安·诺瓦克

6
什么 ?为什么窗口拖移会加载文件?这没有任何意义……
L.Trabacchin

37

经过很多摆弄之后,我发现这是最稳定的解决方案:

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无条件设置都会使我的放置区不再接受任何dnd,无论这些属性是否设置为new。


e.dataTransfer()是完成这项工作的关键,“接受的答案”没有提及。
HoldOffHunger

9

对于jQuery,正确的答案将是:

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

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


9

要仅允许对某些元素进行拖放,可以执行以下操作:

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的检查,否则您仍然可以拖动到文本输入
Andreas Zwerger

2

试试这个:

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

2

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

修改Digital Plane的答案,您可以执行以下操作:

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);

1
有什么意义e || event;?在哪里event定义?没关系。看起来它是IE中的全局对象?我在"In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." 这里
1.21吉瓦

2

注意:尽管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(),在同一页面上使用相同策略的其他组件不会相互干扰。


这就像一个魅力!浏览器甚至通过添加一个禁令图标来更改鼠标光标,这真是太棒了!
pti_jul

1

为了建立在其他一些答案中概述的“检查目标”方法的基础上,这是一个更通用/更实用的方法:

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)
hlfrmn

0

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

如果切换到事件捕获阶段,则可以在嵌入式对象接收事件之前获取事件(请注意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上进行了测试。


0

我将一个类选择器用于多个上传区域,所以我的解决方案采用了这种不太纯净的形式

基于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);
        });
    },
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.