1、本文主要实现的功能是文件拖拽到指定标签区域能后获取到相关事件和文件
2、从粘贴板获取到图片信息(主要目的就是实现网页聊天功能能够拖拽发送文件,粘贴发送图片功能)
拖拽事件
js
能够监听到拖拽的事件有drag
、dragend
、dragenter
、dragexit(没有浏览器实现)
、dragleave
、dragover
、dragstart
、drop
,详细的内容可以看MDN。
其中,与拖拽文件相关的事件有dragenter(文件拖拽进)
、dragover(文件拖拽在悬浮)
、dragleave(文件拖拽离开)
、drop(文件拖拽放下)
。
拖拽事件可以绑定到指定的DOM元素上,可以绑定到整个页面中。
var dropEle = document.querySelector('#dropZone');
dropEle.addEventListener('drop', function (e) {
//
}, false);
阻止默认行为
一般来说,我们只需要把处理拖拽文件的业务逻辑写到drop事件中就可以了,为什么还要绑定dragenter
、dragover
、dragleave
这三个事件呢?
因为当你拖拽一个文件到没有对拖拽事件进行处理的浏览器中的时候,浏览器会打开这个文件,比如拖拽一张图片浏览器会打开这个图片,在没有PDF阅读器的时候也可以拖拽一个PDF到浏览器中,浏览器就会打开这个PDF文件。
如果浏览器打开了拖拽的文件,页面就跳走了,我们希望得到拖拽的文件,而不是让页面跳走。上面说到浏览器会打开拖拽的文件是浏览器的默认行为,我们需要阻止这个默认行为,就需要再上述的事件中进行阻止。
如果你只使用了drop
事件,需要再加入以下一个或者多个事件,防止浏览器默认打开
dropZone.addEventListener("dragenter", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("dragover", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("dragleave", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("drop", function (e) {
e.preventDefault();
e.stopPropagation();
// 处理拖拽文件的逻辑
}
实际上dragenter
不阻止默认行为也不会触发浏览器打开文件,为了防止某些浏览器可能有的兼容性问题,把拖拽周期中的所有的事件都阻止默认行为并且阻止了事件冒泡。
在我实际测试的时候 将文件拖入到可编辑区域的时候 drop
是没问题,浏览器不会执行浏览器的默认程序,非编辑状态的标签是会打开的。
获得拖拽的文件
我们会在drop
这个事件的回调中的事件对象能够得到文件对象。
在事件对象中,一个e.dataTransfer
这样的属性,它是一个DataTransfer类型的数据,有如下的属性
属性 | 类型 | 说明 |
---|---|---|
dropEffect | String | 用来hack某些兼容性问题 |
effectAllowed | String | 暂时不用 |
files | FileList | 拖拽的文件列表 |
items | DataTransferItemList | 拖拽的数据(有可能是字符串) |
types | Array | 拖拽的数据类型 该属性在Safari下比较混乱 |
在Chrome中我们用items
对象获得文件,其他浏览器用files
获得文件,主要是为了处理拖拽文件夹的问题,最好不允许用户拖拽文件夹,因为文件夹内可能还有文件夹,递归上传文件会很久,如果不递归查找,只上传目录第一层级的文件,用户可能以为上传功能了,但是没有上传子目录文件,所以还是禁止上传文件夹比较好,后面我会说要怎么处理。
测试代码
drap
接入到angularjs 中, 功能比较独立所以就把他写成指令
.directive('dropFile',['$log','Tools',function($log,Tools){
return{
restrict:'A',
link:function (scope,element,attrs) {
//为了防止浏览器默认打开文件
angular.element(element).on("dragover",function (e) {
e.preventDefault();
});
angular.element(element).on("drop", function (e) {
scope.imgs=[];
scope.files=[];
e.preventDefault();
e.stopPropagation();
var df = e.originalEvent.dataTransfer;//这个地方和javascript 有些不一样需要注意否则取不到值
var dropFiles = []; // 存放拖拽的文件对象
if (df.items !== undefined) {
// Chrome有items属性,对Chrome的单独处理
for (var i = 0; i < df.items.length; i++) {
var item = df.items[i];
// 用webkitGetAsEntry禁止上传目录
if (item.kind === "file" && item.webkitGetAsEntry().isFile) {
var file = item.getAsFile();
dropFiles.push(file);
}
}
} else {
for (var i = 0; i < df.files.length; i++) {
if (dropFile.type) {
// 如果type不是空串,一定是文件
dropFiles.push(dropFile);
} else {
try {
var fileReader = new FileReader();
fileReader.readAsDataURL(dropFile.slice(0, 3));
fileReader.addEventListener('load', function (e) {
console.log(e, 'load');
dropFiles.push(dropFile);
}, false);
fileReader.addEventListener('error', function (e) {
console.log(e, 'error,不可以上传文件夹');
}, false);
} catch (e) {
console.log(e, 'catch error,不可以上传文件夹');
}
}
}
}
//拿到文件去做相关业务操做
if(dropFiles.length>0){
var data = {};
data["files"] = dropFiles;
scope.$broadcast("$$dropFile",data);
}
});
}
}
}]);
从粘贴板获取图片
需要标签监听paste
事件、angularjs
中有ng-paste
事件监听,我们直接使用即可
$scope.copyFile=function (event) {
if ( event.clipboardData || event.originalEvent ) {
//not for ie11 某些chrome版本使用的是event.originalEvent
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
if ( clipboardData.items ) {
// for chrome
var items = clipboardData.items, len = items.length, blob = null;
//在items里找粘贴的image,据上面分析,需要循环
for (var i = 0; i < len; i++) {
if (items[i].type.indexOf("image") !== -1) {
//getAsFile() 此方法只是living standard firefox ie11 并不支持
blob = items[i].getAsFile();
var data = {};
var files = [];
files.push(blob);
//业务逻辑
data["files"] = files;
$scope.$broadcast("$$dropFile",data);
event.stopPropagation();
event.preventDefault();
}
}
} else {
//for firefox
$log.log("您的浏览器不支持功能");
}
}
}
参考博客
- js控制文件拖拽,获取拖拽内容。
- js实现ctrl+v粘贴上传图片(兼容chrome、firefox、ie11)