在日常開發(fā)中,我們經(jīng)常看到一些網(wǎng)站支持“拖拽上傳”功能,比如你打開某個文件管理后臺,可以直接把本地文件拖進頁面,它就自動上傳了。
這個體驗非常絲滑,也顯得“高級”。
但你有沒有想過:這個功能到底是怎么實現(xiàn)的?
今天我們就來一起拆解一下:JS 拖拽上傳的完整原理與實現(xiàn)方式。
?一、什么是拖拽上傳?
簡單來說,拖拽上傳 就是讓用戶可以拖動文件到瀏覽器頁面的某個區(qū)域,自動觸發(fā)上傳,而不是通過 <input type="file">
的傳統(tǒng)點擊方式。
雖然本質(zhì)上最終還是上傳文件,但用戶交互方式變了,這就涉及到瀏覽器的一套“拖放事件機制”。
??二、核心原理拆解
想實現(xiàn)拖拽上傳,需要用到以下幾個關(guān)鍵點:
1. 拖拽事件的監(jiān)聽
瀏覽器提供了一系列拖拽相關(guān)事件:
| |
dragenter | |
dragover | 拖動過程中持續(xù)觸發(fā)(必須阻止默認) |
dragleave | |
drop | 在目標區(qū)域松開鼠標時觸發(fā)(即“放下”) |
2. 阻止默認行為(非常關(guān)鍵)
瀏覽器默認會在 drop
時打開文件,所以我們必須阻止默認行為:
e.preventDefault();
否則你一拖文件進頁面,頁面就直接跳轉(zhuǎn)去預覽了...
3. 從 drop
中讀取文件
當你在 drop
事件里獲取到事件對象 e
時,可以通過:
e.dataTransfer.files
來讀取用戶拖進來的文件列表。
這和傳統(tǒng) input 方式中的 input.files
是一樣的東西,只不過來源不同。
???三、完整實現(xiàn)思路(HTML + JS)
下面我們來寫個最簡版的拖拽上傳 DEMO,只用于展示流程:
<div id="drop-area" style="width: 300px; height: 200px; border: 2px dashed #ccc; text-align: center; line-height: 200px;">
拖拽文件到這里上傳
</div>
<script>
const dropArea = document.getElementById('drop-area');
// 拖進來:添加高亮
dropArea.addEventListener('dragenter', (e) => {
e.preventDefault();
dropArea.style.borderColor = '#3eaf7c';
});
// 拖動中:必須阻止默認,才能觸發(fā) drop
dropArea.addEventListener('dragover', (e) => {
e.preventDefault();
});
// 拖出:恢復樣式
dropArea.addEventListener('dragleave', (e) => {
e.preventDefault();
dropArea.style.borderColor = '#ccc';
});
// 放下文件:讀取文件
dropArea.addEventListener('drop', (e) => {
e.preventDefault();
dropArea.style.borderColor = '#ccc';
const files = e.dataTransfer.files;
// 遍歷文件列表
for (let file of files) {
console.log('文件名:', file.name);
uploadFile(file); // 上傳邏輯
}
});
// 模擬上傳函數(shù)
functionuploadFile(file) {
const formData = newFormData();
formData.append('file', file);
fetch('/api/upload', {
method: 'POST',
body: formData
}).then(res => {
if (res.ok) {
console.log(`${file.name} 上傳成功`);
} else {
console.error(`${file.name} 上傳失敗`);
}
});
}
</script>
?四、進階點補充
1. 拖拽區(qū)域可以是任意 DOM 元素,不一定是整個頁面。
你甚至可以讓 <textarea>
接收拖拽的文件(比如 markdown 圖片粘貼上傳)。
2. e.dataTransfer.items
可以識別是不是文件夾
for (let item of e.dataTransfer.items) {
if (item.kind === 'file') {
const entry = item.webkitGetAsEntry();
if (entry.isDirectory) {
console.log('是個文件夾');
}
}
}
3. 拖拽上傳配合組件庫用法(如 Vue + Element Plus)
Element Plus 的 <el-upload>
組件就內(nèi)置了 drag 模式,原理跟我們上面說的是一樣的,只是封裝好了。
??五、總結(jié)
我們回顧一下拖拽上傳的核心實現(xiàn):
- 1. 監(jiān)聽
dragenter
, dragover
, drop
等事件; - 3. 從
e.dataTransfer.files
讀取文件; - 4. 手動構(gòu)造
FormData
上傳文件;
雖然概念簡單,但這就是一個非常實用的 Web 技能,做后臺系統(tǒng)、CMS、富文本編輯器、前端可視化工具時經(jīng)常會用到。