我正在向我的页面添加一个html5拖放上传器。
当文件被放入上传区域时,一切都很好。
但是,如果我不小心将文件拖放到上传区域之外,浏览器会将本地文件加载为新页面。
如何防止此行为?
谢谢!
答案 0 :(得分:249)
您可以在所有dragover和drop事件上调用preventDefault()
的窗口中添加一个事件监听器。
例如:
window.addEventListener("dragover",function(e){
e = e || event;
e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
},false);
答案 1 :(得分:26)
经过大量的摆弄后,我发现这是最稳定的解决方案:
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>
在窗口上无条件地设置effectAllow
和dropEffect
会导致我的放置区不再接受任何d-n-d,无论属性是否设置为新。
答案 2 :(得分:8)
要仅允许在某些元素上进行拖放,您可以执行以下操作:
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);
答案 3 :(得分:8)
对于jQuery,正确答案是:
$(document).on({
dragover: function() {
return false;
},
drop: function() {
return false;
}
});
此处return false
的行为为event.preventDefault()
和event.stopPropagation()
。
答案 4 :(得分:2)
试试这个:
document.body.addEventListener('drop', function(e) {
e.preventDefault();
}, false);
答案 5 :(得分:2)
默认情况下,防止所有拖放操作可能不是您想要的。可以检查拖动源是否是外部文件,至少在某些浏览器中是这样。我已经包含了一个函数来检查拖动源是否是此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);
答案 6 :(得分: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])
);
答案 7 :(得分:0)
我有一个填充页面宽度和高度的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&#39;答案),页面成为拖动目标,它可以防止对象嵌入捕获事件然后加载我们的图像:
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上测试过。
答案 8 :(得分: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);
});
},
答案 9 :(得分:0)
注意:尽管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(),在同一页面上使用相同策略的其他组件不会相互干扰。