--- 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)