以前写了一篇文章 《前端粘贴复制还能这样玩》,其中涉及到DataTransfer,下面我们来了解一下DataTransfer对象。
DataTransfer对象存在哪些事件对象中
目前就发现拖拽相关事件和paste事件的事件对象中包含DataTransfer对象。
DataTransfer对象介绍
在mdn中介绍DataTransfer对象用于保存拖动并放下(drag and drop)过程中的数据。但是paste事件对象中clipboardData也是一个DataTransfer。
dropEffect获取当前选定的拖放操作类型或者设置的为一个新的类型。值必须为none(项目可能禁止拖放),copy(在新位置生成源项的副本),link(在新位置建立源项目的链接) 或move(将项目移动到新位置)。它设置的属性表示鼠标拖动的视觉效果,所以我们可以在dragover, dragenter中进行设置该值。effectAllowed设置源元素(被拖动的元素)允许的拖动效果。none(此项表示不允许放下)、copy(源项目的复制项可能会出现在新位置。)、copyLink(允许 copy 或者 link 操作。)、copyMove(允许 copy 或者 move 操作。)、link(可以在新地方建立与源的链接。)、linkMove(允许 link 或者 move 操作。)、move(一个项目可能被移动到新位置。)、all(允许所有的操作。)、uninitialized(效果没有设置时的默认值,则等同于 all。) 设置对应的属性有对应的效果。即可以限制我们拖动的结果。所以一般在dragstart中进行设置。
effectAllowed和dropEffect相互影响。二者的值需要设置交集。否则拖动是没有效果的。 想要查看效果可以看mdn demo
files获取拖动和粘贴的文件(File)对象列表。这里的文件数据也会保存到items中,以DataTransferItem对象形式存在。items拖动和粘贴所产生的数据(DataTransferItem)集合。内部包含kind, type两个属性。types拖动和粘贴所产生的数据的类型。例如文本(text/plain), 图片,文件资源(Files)setData(type, value)设置拖动所需要的数据源,会加入到items属性集合中。注意设置相同类型type的数据会覆盖。
getData(type)通过指定的type获取对应的数据源。clearData(type)清除指定type的数据源,如果不指定type则清空所有数据,该方法只有在dragstart事件中使用有效。setDragImage(imgElement, xOffset, yOffset)修改拖动时的图像,一般没啥用,除非你用到了。
DataTransferItem(DataTransfer.items每一项)对象介绍
最主要的是我们应该关注items中的数据对象,即DataTransferItemList对象,它内部包含着粘贴和拖拽的DataTransferItem对象。
我们来看下DataTransferItem对象中属性和方法
kind拖拽或者粘贴项的种类,string或是file。type拖拽或者粘贴项的类型,一般是一个 MIME 类型。
getAsFile()如果DataTransferItem是一个文件,那DataTransferItem.getAsFile()方法将返回拖拽或者粘贴项数据的File对象。如果拖拽项的数据不是一个文件,则返回null。
getAsFileSystemHandle()返回一个Promise, 可以用于判断当前粘贴和拖拽本地磁盘资源项是文件(FileSystemFileHandle)还是文件夹(FileSystemDirectoryHandle)。相比较直接通过kind, type可以更具体的参看类型。
如果是文件可以调用getFile()方法获取文件对象。如果是文件夹可以调用getFileHandle(),返回指定名称的FileSystemFileHandle对象然后再进行处理。
getAsString(callback)如果DataTransferItem是一个字符串,可以在回调中获取粘贴或者拖拽的字符串的值。如果是文件资源那么回调将不会执行。
粘贴的测试代码
<div contenteditable="true">
我是可输入文本的div
</div>
<script>
const div = document.getElementsByTagName("div")[0]
// 这个事件只作用于可编辑的dom元素上。
// 如果元素都没有设置contenteditable="true",那么将作用于整个网页。
div.addEventListener("paste", async function (e) {
console.log("粘贴", e.clipboardData.items[0])
// console.log("查看当前粘贴本地的内容是文件还是文件夹", await e.clipboardData.items[0].getAsFileSystemHandle())
// e.clipboardData.items[0].getAsString((res => {
// console.log("获取粘贴的字符串值", res)
// }))
// console.log("粘贴文件返回文件对象", e.clipboardData.items[0].getAsFile())
const fileOrDir = await e.clipboardData.items[0].getAsFileSystemHandle()
if(fileOrDir.kind === "file") {
console.log("获取文件对象", await fileOrDir.getFile())
}
})
</script>
拖拽的测试代码
<div id="img-container"></div>
<img id="img" src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" alt="" draggable="true" width="100">
<script>
/**
*
*
- dragstart`: 开始拖拽对象时触发。在这里开始传递一些源数据。`e.dataTransfer.setData()`
*
- `dragover`: 当被拖拽元素**未离开**可释放目标元素上时,触发该事件。**在拖拽的过程中。**
- `dragleave`: 当被拖拽元素**离开**可释放目标元素上时,触发该事件。
- `drop`: 拖拽元素拖拽到可释放目标对象释放后触发。即在这边获取拖拽元素时传入的一些源数据,做一些其他的逻辑处理。通过`e.dataTransfer.getData()`来获取对应的属性。
*
**/
const target = document.getElementById("img-container")
const source = document.getElementById("img")
source.ondragstart = function(e) {
console.log("开始")
e.dataTransfer.setData('text/html', "覆盖拖动时产生的text/html类型数据");
e.dataTransfer.setData('text/plain', 'plain text');
// 发送数据
e.dataTransfer.setData('imgName', "test")
e.dataTransfer.setData('imgType', "png")
e.dataTransfer.setData('imgPath', e.target.currentSrc)
e.dataTransfer.clearData()
}
// target.ondragenter = function(e) {
// console.log("e", e)
// }
// 进入目标元素时触发
target.addEventListener("dragover", (event) => {
console.log("进入目标元素移动时触发")
// prevent default to allow drop
event.preventDefault();
});
// 在使用drop事件之前,需要注册dragover事件。并且需要阻止默认事件。
target.ondrop = async function(e) {
console.log("拖拽", e.dataTransfer.items, e.dataTransfer.items[0], e.dataTransfer.items[1], e.dataTransfer.items[2], e.dataTransfer.items[3], e.dataTransfer.items[4])
console.log("查看当前拖拽本地的内容是文件还是文件夹", await e.dataTransfer.items[0].getAsFileSystemHandle())
e.dataTransfer.items[0].getAsString((res => {
console.log("获取拖拽的字符串值0", res)
}))
e.dataTransfer.items[1].getAsString((res => {
console.log("获取拖拽的字符串值1", res)
}))
e.dataTransfer.items[2].getAsString((res => {
console.log("获取拖拽的字符串值2", res)
}))
e.dataTransfer.items[3].getAsString((res => {
console.log("获取拖拽的字符串值2", res)
}))
console.log("拖拽数据中的文件", e.dataTransfer.files, e.dataTransfer.files[0])
console.log("拖拽文件返回文件对象", e.dataTransfer.items[0].getAsFile())
console.log("通过getData(type)获取数据源", e.dataTransfer.getData('text/html'))
// console.log("e", e, e.dataTransfer.files[0], e.dataTransfer.items[0], e.dataTransfer.types[0])
// // 在可拖拽的区域拖拽到指定的对象时触发
// const imgName = e.dataTransfer.getData('imgName')
// const imgType = e.dataTransfer.getData('imgType')
// const imgPath = e.dataTransfer.getData('imgPath')
// // 我们拖动文件到别的文件夹时,只是将文件对象加入到目的文件列表中。
// const img = document.createElement("img")
// img.setAttribute("src", imgPath)
// img.setAttribute("name", imgName)
// target.appendChild(img)
// img.style.width = "100px"
// img.style.height = "100px"
// document.body.removeChild(source)
}
</script>
了解上面的这些操作我们就很容易的拿到文件对象上传给服务器了。
// index.html
<div contenteditable="true">
我是可输入文本的div
</div>
<script>
const div = document.getElementsByTagName("div")[0]
div.addEventListener("paste", async function (e) {
console.log("粘贴", e.clipboardData.items[0], e.clipboardData.files[0])
const formData = new FormData()
formData.append("file", e.clipboardData.files[0])
let config = {
headers: {'Content-Type': 'multipart/form-data'}
}
axios.post("http://127.0.0.1:3000/login/file", formData, config).then(res => {
console.log("res",res)
})
})
</script>
后端我们是通过nestjs进行处理文件上传的。这里需要配合multer库去实现。
@Post('file')
@UseInterceptors(
AnyFilesInterceptor({
storage: diskStorage({
destination: 'uploads',
filename: (req, file, cb) => {
const randomName = Array(32)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('');
return cb(null, `${randomName}${extname(file.originalname)}`);
},
}),
}),
)
uploadCopyImgs(@UploadedFiles() files: any) {
console.log(files);
return `文件上传成功文件名为: ${files[0].filename}`;
}
往期年度总结
往期文章
- Nest装饰器全解析
- Nest世界中的AOP
- Nestjs如何解析http传输的数据
- 如何理解js的DOM事件系统
- 半年没看vue官网,3.5刚刚发布,趁机整理下
- 啊,你还在找一款强大的表格组件吗?
- 前端大量数据层级展示及搜索定位预览
- 如何从0开始认识m3u8(提取,解析及下载)
- 展示大量数据节点(tree),引发的一次性能排查
- ts装饰器的那点东西
- 这是你所知道的ts类型断言和类型守卫吗?
- TypeScript官网内容解读
- 经常使用ts的你,知道这些内容?
- 你有了解过原生css的scope?
- 现在比较常用的移动端调试你知道哪些?
- 众多跨标签页通信方式,你知道哪些?(二)
- 众多跨标签页通信方式,你知道哪些?
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论, 支持一下博主~
公众号:全栈追逐者,不定期的更新内容,关注不错过哦!