💥【Chatterbox(话匣子)】如何实现拖拽、粘贴文件上传?
背景
获取图片文件
const images = ref([])
const reader = () => {
images.value = []
for (let i = 0; i < files.value.length; i++) {
const file = files.value[i];
const reader = new FileReader()
reader.onload = (e) => {
images.value.push(e.target.result)
}
reader.readAsDataURL(file);
}
}
复制图片 粘贴上传
我们先来实现一个简单的-粘贴上传。既然是粘贴,那我们就要监听到粘贴的事件。那我们就可以通过监听paste
事件,来获取到粘贴的内容。
el.addEventListener("paste", (event) => {});
事件处理器可以通过调用事件 clipboardData
属性的下的items
来访问剪贴板内容。(MDN:可以通过调用事件 clipboardData
属性的 getData()
方法来访问剪贴板内容。)
const pasteHandler = (e) => {
console.log('🚲 触发粘贴事件:', e);
files.value = []
const items = (e.clipboardData || e.originalEvent.clipboardData).items
for (const index in items) {
const item = items[index]
if (item.kind === 'file') {
files.value.push(item.getAsFile())
}
}
reader()
}
至此粘贴文件上传的功能就已经实现了。
演示
拖拽图片上传
接下来实现文件拖拽到目标区域获取对应的文件。既然是拖拽,就要先了解关于拖拽的相关API。这边只介绍我用到的相关API,其它的可以到MDN上自行阅读。👉drag
监听事件
dragenter
:在可拖动的元素或者被选择的文本进入一个有效的放置目标时触发。dragover
:在可拖动的元素或者被选择的文本被拖进一个有效的放置目标时(每几百毫秒)触发。drop
:在元素或文本选择被放置到有效的放置目标上时触发。为确保drop
事件始终按预期触发,应当在处理dragover
事件的代码部分始终包含preventDefault()
调用。dragleave
:在拖动的元素或选中的文本离开一个有效的放置目标时被触发。
event.preventDefault()
这个方法用于阻止事件的默认行为。比如我们正常拖动一个图片文件到浏览器中,它会在一个新的标签页中打开这个图片。我们在实现该上传功能的时候是不希望他打开这个图片,就需要调用该方法来阻止它的默认行为。
event.stopPropagation()
这个方法用于阻止事件冒泡。当某个元素被触发某个事件(如点击事件)时,这个事件会从最深的节点(或目标节点)开始,然后逐级向上传播到最少特定节点。调用该方法可以阻止这种冒泡行为,使得父元素或其他祖先元素无法接收到这个事件。
阻止默认行为
经过实验发现只要在拖拽的dragover
、drop
两个事件中全都调用event.preventDefault()
就可以阻止浏览器在新的页签中打开图片。
const dragHandle = () => {
const events = ['dragover', 'drop']
events.forEach(event => {
refUpload.value.addEventListener(event, e => {
e.preventDefault()
})
})
}
获取拖拽的文件
在拖拽过程中,我们需要在用户在特定区域放下鼠标的时候获取到对应的文件,我们可以drop
事件中监听到用户放置行为,并在event.dataTransfer.files
可以获取到对应的文件。
const dropHandler = (e) => {
files.value = e.dataTransfer.files
reader()
}
refUpload.value.addEventListener('drop', dropHandler)
到此拖拽上传的基本功能都已经实现完成。
优化
在我们实际开发过程中,必然会加上一些样式来提高用户体验。上文中提到了好几个监听事件,到这里还没有用到,下面我们在上方的基础上在进行优化。比方说,当用户拖拽文件到目标区域的时候给目标区域的加上边框。这就比较简单了,当触发dragenter
事件的时候,给目标区域的DOM加上对应样式即可。
const addHighlight = () => {
refUpload.value.classList.add('highlight')
}
refUpload.value.addEventListener('dragenter', addHighlight)
当拖拽文件的离开目标区域或者在目标区域放下文件的时候,我们也要对应的给目标区域的DOM移除对应的样式。即在dragleave
和drop
事件触发时移除样式。
const removeHighlight = () => {
refUpload.value.classList.remove('highlight')
}
refUpload.value.addEventListener('dragleave', removeHighlight)
const dropHandler = (e) => {
removeHighlight()
files.value = e.dataTransfer.files
reader()
}
refUpload.value.addEventListener('drop', dropHandler)
拖拽文件到子元素的问题
这里有小坑,当我们的目标区域内还存在子元素的时候。拖动文件到达子元素的时候会触发dragleave
事件,相反会触发dragenter
。在我们实际开发中,是不希望进入子元素的时候触发dragleave
事件的,那样会移除上边给目标区域添加的样式。那我们该如何做呢?下方贴一下我的实现代码:
let lastElement = null
refUpload.value.addEventListener('dragenter', e => {
lastElement = e.target
addHighlight()
})
refUpload.value.addEventListener('dragleave', e => {
if (e.target === lastElement) {
removeHighlight()
}
})
我通过记录进入时候的元素,当触发dragleave
的时候再移除对应的样式。
演示
以上就是本次分享的内容,由于完整的代码内容过多就不再此粘贴,附上拖拽、粘贴文件上传源码
🦀🦀感谢看官看到这里,如果觉得文章不错的话,可以给小生的几个开源项目点个Star⭐!
- 基于 Vue3 + Element-plus 管理后台基础功能框架
- 预览:admin.gumingchen.icu
- Github:github.com/gmingchen/a…
- Gitee:gitee.com/shychen/agi…
- 基础版后端:github.com/gmingchen/j…
- 文档:admin.gumingchen.icu/doc/
- 基于 Vue3 + Element-plus + websocket 即时聊天系统
- 预览:chatterbox.gumingchen.icu/
- Github:github.com/gmingchen/c…
- Gitee:gitee.com/shychen/cha…
- 基于 node 开发的后端服务:github.com/gmingchen/n…
转载自:https://juejin.cn/post/7389935779333324811