---
disqus: andrew-hackmd
---
# JS HTML5拖拽上傳圖片預覽
###### tags : `w3HexSchool` . `javascript`
> 有時可以看到 , 將圖片拖曳至此上傳圖片的區塊 ( ex : HackMD 的編輯區塊 ) , 這個東西是如何實現的呢 ?
![](https://i.imgur.com/bIb9Hj0.gif)
## 重要 API : File API . Drag API
> Tree 查過之後 , 發現其中有 2 個重要的 Browser API 需要了解後才能製作
- 檔案操作 : File API
```javascript=
const reader = new FileReader(); // 建立檔案讀取器 , 將上傳的圖片資料取出
reader.onload = (e => img.src = e.target.result); // 註冊讀取完成後 , 執行的動作
reader.readAsDataURL(file); // 取出 file 中的 base64 格式 data
```
- 拖曳相關事件 : Drag API
- dragenter : 拖曳圖片 `進入` 區塊時觸發
- dragleave : 拖曳圖片 `離開` 區塊時觸發
- dragover : 拖曳圖片於區塊中 `移動` 時觸發
- drop : 於區塊中 `放開` 拖曳的圖片時觸發
> 上述 Drap API 預設動作皆為 `轉址到檔案位置` ,
因此需要 防止預設事件 `preventDefault` 和 終止事件傳導 `stopPropagation`
## 實作開始
> 了解 API 的基礎原理後 , 讓我們開始製作一個 圖片拖曳的預覽區塊吧 !
![](https://i.imgur.com/bIb9Hj0.gif)
### 製作步驟概要
- 將預設的 dragenter . dragover . drop 行為取消掉
- 實作 drop 行為 , 並處理上傳的 files 陣列
- 將預計上傳的圖片顯示出來
- 圖片向伺服器儲存
### 先設定骨架以及長相
> 建立一個 upload_zone
```htmlmixed=
<div id="upload_zone" class="upload_zone">
請將要上傳的圖片拖曳至此
</div>
```
> 將 upload_zone 上底色
```css=
.upload_zone {
width: 200px;
height: 200px;
margin: 20px;
padding: 10px;
cursor: pointer;
background-color: aqua;
}
```
### Drag 相關的 API
> 建立 `dragenter` . `dragover` . `drop` 的函數 , 以防止預設事件 & 終止事件傳導 ( 避免圖片拖曳造成的轉址問題 )
```javascript=
function dragenter(e) {
e.stopPropagation(); //終止事件傳導
e.preventDefault(); //終止預設行為
}
function dragover(e) {
e.stopPropagation(); //終止事件傳導
e.preventDefault(); //終止預設行為
}
function drop(e) {
e.stopPropagation(); //終止事件傳導
e.preventDefault(); //終止預設行為
}
```
> 註冊 `dragenter` . `dragover` . `drop` 事件到 `upload_zone`
```javascript=
const dropbox = document.getElementById("upload_zone");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);
```
> 設定 `dragenter` 事件觸發後的樣式 A
```css=
.upload_zone_enter {
border: 10px dashed black;
background-clip: content-box;
}
```
> `dragenter` 時追加樣式 A , `dragleave` 時移除樣式 A 到 `upload_zone`
```javascript=
const dragleave = () => dropbox.classList.remove("upload_zone_enter");
function dragenter(e) {
+ dropbox.classList.add("upload_zone_enter");
e.stopPropagation(); //終止事件傳導
e.preventDefault(); //終止預設行為
}
dropbox.addEventListener("dragleave", dragleave, false);
```
### 實作 Drop 行為並顯示拖曳的圖片
> 將 Drag 的圖片顯示到 preview 中
```javascript=
function handleFiles(files) {
for (var i = 0; i < files.length; i++) {
const file = files[i];
const imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
const img = document.createElement("img");
img.classList.add("preview_image");
img.file = file;
preview.appendChild(img); // 將圖片加到 preview 上
const reader = new FileReader();
reader.onload = (e => img.src = e.target.result);
reader.readAsDataURL(file);
}
}
function drop(e) {
e.stopPropagation();
e.preventDefault();
+ const dt = e.dataTransfer;
+ const files = dt.files; // 取得被拖曳的圖片
+ handleFiles(files);
+ dropbox.classList.remove("upload_zone_enter");
}
```
> 設定 preview 中的圖片樣式
```css=
.preview_image {
width: 200px;
height: 200px;
}
```
> 完成上方設定後 , 將會得到下方圖片之效果
![](https://i.imgur.com/bIb9Hj0.gif)
### 成品展示
<iframe height="500" style="width: 100%;" scrolling="no" title="JS HTML5拖拽上傳圖片預覽" src="https://codepen.io/andrew781026/embed/VwvQxGo?height=265&theme-id=dark&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/andrew781026/pen/VwvQxGo'>JS HTML5拖拽上傳圖片預覽</a> by 王澍
(<a href='https://codepen.io/andrew781026'>@andrew781026</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
## 參考資料
- [MDN 教學:在網頁應用程式中使用本地檔案](https://developer.mozilla.org/zh-TW/docs/Web/API/File/Using_files_from_web_applications)