共计 2613 个字符,预计需要花费 7 分钟才能阅读完成。
场景
最近,小明遇到这样一种情况:在网页中上传文件时偶尔页面会崩溃。小明仔细测试了这种情况,发现之前用的一个文件上传组件有一点缺陷,于是,小明决定自己手写一个,样式如下:
图一是没有上传文件时的样式,图二为上传文件后的样式。虚线部分为可拖拽区域,先来看代码:
html 部分
<div id=”app”>
<div class=”content”>
<div class=”drag-area” @dragover=”fileDragover” @drop=”fileDrop”>
<div v-if=”fileName” class=”file-name”>{{fileName}}</div>
<div v-else class=”uploader-tips”>
<span> 将文件拖拽至此,或 </span>
<label for=”fileInput” style=”color: #11A8FF; cursor: pointer”> 点此上传 </label>
</div>
</div>
</div>
<div class=”footer”>
<input type=”file” id=”fileInput” @change=”chooseUploadFile” style=”display: none;”>
<label for=”fileInput” v-if=”fileName” style=”color: #11A8FF; cursor: pointer”> 选择文件 </label>
<button @click=”uploadOk”> 提交 </button>
</div>
</div>
css 部分
* {
font-size: 14px;
}
.drag-area {
height: 200px;
width: 300px;
border: dashed 1px gray;
margin-bottom: 10px;
color: #777;
}
.uploader-tips {
text-align: center;
height: 200px;
line-height: 200px;
}
.file-name {
text-align: center;
height: 200px;
line-height: 200px;
}
js 部分
new Vue({
el: ‘#app’,
data () {
return {
fileName: ”,
batchFile: ”,
MAX_FILE_SIZE: 10 * 1000 * 1000
}
},
methods: {
// 点击上传
chooseUploadFile (e) {
const file = e.target.files.item(0)
if (!file) return
if (file.size > this.MAX_FILE_SIZE) {
return alert(‘ 文件大小不能超过 10M’)
}
this.batchFile = file
this.fileName = file.name
// 清空,防止上传后再上传没有反应
e.target.value = ”
},
// 拖拽上传
fileDragover (e) {
e.preventDefault()
},
fileDrop (e) {
e.preventDefault()
const file = e.dataTransfer.files[0] // 获取到第一个上传的文件对象
if (!file) return
if (file.size > this.MAX_FILE_SIZE) {
return alert(‘ 文件大小不能超过 10M’)
}
this.batchFile = file
this.fileName = file.name
},
// 提交
uploadOk () {
if (this.batchFile === ”) {
return alert(‘ 请选择要上传的文件 ’)
}
let data = new FormData()
data.append(‘upfile’, this.batchFile)
// ajax
}
}
})
核心代码说明
dragover 和 drop 事件
第一个要说的就是拖拽中的这两个事件,因为这两个事件撑起了拖拽上传的核心功能。对于拖拽这个动作而言,有二个核心概念,一个是拖拽元素,还一个是放置目标。这里,我只讲放置目标上的事件,对于拖拽元素的事件,请自行查阅。
那对于放置目标,它有什么事件呢?如下:当某个元素被拖动到一个有效的放置目标上 (如上例中虚线区域) 时,下列事件会依次发生:(1) dragenter(2) dragover(3) dragleave 或 drop 只要有元素被拖动到放置目标上,就会触发 dragenter 事件(类似于 mouseover 事件)。紧随其后的是 dragover 事件,而且在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。如果元素被拖出了放置目标,dragover 事件不再发生,但会触发 dragleave 事件(类似于 mouseout 事件)。如果元素被放到了放置目标中,则会触发 drop 事件而不是 dragleave 事件。
对于本例来说,我们只需要关注 dragover 和 drop 事件。但是 drop 事件却有点调皮,你想监听它,还得进行一些处理,因为默认情况下,元素是不允许放置的,在拖动元素经过某些无效放置目标时,可以看到一种特殊的光标(圆环中有一条反斜线),表示不能放置。如下:如果拖动元素经过不允许放置的元素,那无论用户如何操作,都不会发生 drop 事件。那怎么办呢?我们可以重写 dragover 事件的默认行为,如上例代码中的 e.preventDefault()。细心的同学可能要问了,那 drop 事件中也有 e.preventDefault(),去掉行不行呢?大家可以自行试下。
dataTransfer 对象
可能这个对象看着有些陌生,但是它的作用可不小。比如,你拖动一个图片到目标区域,那目标区域怎么获取这个图片的信息呢?就靠它!它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。在本例中,我们可以通过它来获取拖动中的文件信息。
input 的 change 事件
这个事件其实有坑的,它有这样一个特性,即:上传同一个文件,并不会触发 change 事件,即使该文件内容做过修改。细思极恐!比如,用户要上传一个文档,但是拖拽到虚线区域后发现文档内容还需要修改下,他改完后再拖拽该文档,再提交到服务器,那么他上传到服务器的文档内容却是未修改之前的!所以,我们需要代码 e.target.value = ” 来进行重置处理,这样,每次上传文件都会触发 change 事件。