Javascript 防止浏览器加载拖放文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6756583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 23:04:01  来源:igfitidea点击:

Prevent browser from loading a drag-and-dropped file

javascriptjqueryhtmldrag-and-drop

提问by Travis

I'm adding an html5 drag and drop uploader to my page.

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

When a file is dropped into the upload area, everything works great.

当文件被放入上传区域时,一切正常。

However, if I accidentally drop the file outside of the upload area, the browser loads the local file as if it is a new page.

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

How can I prevent this behavior?

我怎样才能防止这种行为?

Thanks!

谢谢!

回答by Digital Plane

You can add a event listener to the window that calls preventDefault()on all dragover and drop events.
Example:

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

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

回答by Axel Amthor

After a lot of fiddling around, I found this to be the stablest solution:

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

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>

Setting both effectAllowand dropEffectunconditionally on the window causes my drop zone not to accept any d-n-d any longer, regardless whether the properties are set new or not.

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

回答by VisioN

For jQuery the correct answer will be:

对于 jQuery,正确答案是:

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

Here return falsewill behave as event.preventDefault()and event.stopPropagation().

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

回答by enthus1ast

To allow drag-and-drop only on some elements, you could do something like:

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

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

回答by moe

try this:

尝试这个:

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

回答by Shannon Matthews

Preventing all drag and drop operations by default might not be what you want. It's possible to check if the drag source is an external file, at least in some browsers. I've included a function to check if the drag source is an external file in this StackOverflow answer.

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

Modifying Digital Plane's answer, you could do something like this:

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

回答by Superole

Note:Although the OP did not ask for an Angular solution, I came here looking for that. So this is to share what I found to be a viable solution, if you use Angular.

注意:虽然 OP 没有要求提供 Angular 解决方案,但我是来这里寻找的。所以这是为了分享我发现的一个可行的解决方案,如果你使用 Angular。

In my experience this problem first arises when you add file drop functionality to a page. Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone.

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

In my solution the drop zone is an input with a class, but any unambiguous selector works.

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

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';
    }
  }
}

The listeners are added/removed automatically when component is created/destroyed, and other components using the same strategy on the same page do not interfere with each other due to the stopPropagation().

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

回答by scott_trinh

To build on the "check the target" method outlined in a few other answers, here is a more generic/functional method:

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

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

Called like:

调用如下:

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

回答by 1.21 gigawatts

I have an HTML object(embed) that fills the width and height of the page. The answer by @digital-plane works on normal web pages but not if the user drops onto an embedded object. So I needed a different solution.

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

If we switch to using the event capture phasewe can get the events before the embedded object receives them (notice the truevalue at the end of the event listener call):

如果我们切换到使用事件捕获阶段,我们可以在嵌入对象接收事件之前获取事件(注意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);

Using the following code (based on @digital-plane's answer) the page becomes a drag target, it prevents object embeds from capturing the events and then loads our images:

使用以下代码(基于@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);

Tested on Firefox on Mac.

在 Mac 上的 Firefox 上测试。

回答by hngr18

I am using a class selector for multiple upload areas so my solution took this less pure form

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

Based on Axel Amthor's answer, with dependency on jQuery (aliased to $)

基于 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);
        });
    },