當(dāng)我們學(xué)習(xí)了 html 提供的原生拖放(drag & drop)后,是時候想一想這個東西可以用來作什么,可以在什么時候使用,使用的場景等等
場景分析
當(dāng)我們在注冊成功一個賬戶時,一般網(wǎng)站會讓我們上傳我們的用戶頭像,或者在實名認(rèn)證的時候會涉及到身份證圖片上傳到等,這時候我們可以使用input提供的file屬性進行選擇本地文件進行上傳。

我們再想一下,當(dāng)在電腦端的情況下,當(dāng)用戶打開文件選擇框時再尋找圖片對應(yīng)的文件夾,再進行選取文件的時候是不是會有點麻煩呢?我們可不可以讓用戶找到圖片文件,直接引入實現(xiàn)上傳呢?答案是可以的。
怎么做
經(jīng)過這些分析后,我們可以嘗試使用 HTML5 提供的拖拽,使得目標(biāo)元素增加讀取文件功能,然后使用 ajax 實現(xiàn)圖片上傳。
談一談我們需要使用到的技術(shù):
- Drag & Drop: HTML5 基于拖拽的事件機制
- File API: 可以很方便的讓 Web 應(yīng)用訪問文件對象,F(xiàn)ile API 包括 FileList、Blob、File、FileReader、URI scheme,本文主要講解拖拽上傳中用到的 FileList 和 FileReader 接口。
- FormData: FormData 是基于 XMLHttpRequest Level 2 的新接口,可以方便 web 應(yīng)用模擬 Form 表單數(shù)據(jù),最重要的是它支持文件的二進制流數(shù)據(jù),這樣我們就能夠通過它來實現(xiàn) AJAX 向后端發(fā)送文件數(shù)據(jù)了。
HTML5 拖拽事件
關(guān)于 Drag & Drop 拖拽事件,之前我寫過一篇專門介紹的文章,HTML5-拖拽,大家有興趣的話可以點擊鏈接查看,我在這里就不在多啰嗦了~下面直接出拖拽上傳的簡要代碼示例
var oDragWrap = document.body; //拖進 oDragWrap.addEventListener( "dragenter", function(e) { e.preventDefault(); }, false ); //拖離 oDragWrap.addEventListener( "dragleave", function(e) { dragleaveHandler(e); }, false ); //拖來拖去 , 一定要注意dragover事件一定要清除默認(rèn)事件 //不然會無法觸發(fā)后面的drop事件 oDragWrap.addEventListener( "dragover", function(e) { e.preventDefault(); }, false ); //扔 oDragWrap.addEventListener( "drop", function(e) { dropHandler(e); }, false ); var dropHandler = function(e) { //將本地圖片拖拽到頁面中后要進行的處理都在這 };
獲取文件數(shù)據(jù) HTML5 File API
File API 中的 FileReader 接口,作為 File API 的一部分,F(xiàn)ileReader 專門用來讀取文件。我們在這里主要介紹一些 File API 中的 FileList 接口,它主要通過兩個途徑獲取本地文件列表,一是<input type="file"/>的表單形式,另一種則是e.dataTransfer.files拖拽事件傳遞的文件信息。
var fileList = e.dataTransfer.files;
使用 files 方法將會獲取到拖拽文件的數(shù)組形式的數(shù)據(jù),每個文件占用一個數(shù)組的索引,如果索引不存在文件數(shù)據(jù),將返回 Null。可以通過length屬性獲取文件的數(shù)量。
var fileNum = fileList.length;
拖拽上傳需要注意的是需要判斷兩個條件
- 拖拽的是文件而不是頁面的元素
- 拖拽的是圖片而不是其他類型的文件,可以通過 file.type 屬性獲取文件的類型
// 檢測是否是拖拽文件到頁面的操作 if (fileList.length === 0) { return; } // 檢測文件是不是圖片 if (fileList[0].type.indexOf("image") === -1) { return; }
下面我們看看結(jié)合之前的拖拽事件,來實現(xiàn)拖拽圖片并在頁面中預(yù)覽
var dropHandler = function(e) { e.preventDefault(); //獲取文件列表 var fileList = e.dataTransfer.files; //檢測是否是拖拽文件到頁面的操作 if (fileList.length == 0) { return; } //檢測文件是不是圖片 if (fileList[0].type.indexOf("image") === -1) { return; } //實例化file reader對象 var reader = new FileReader(); var img = document.createElement("img"); reader.onload = function(e) { img.src = this.result; oDragWrap.AppendChild(img); }; reader.readAsDataURL(fileList[0]); };

當(dāng)完成以上操作后,相信你可以成功的完成了拖拽圖片預(yù)覽的操作。當(dāng)你查看 img 標(biāo)簽時會發(fā)現(xiàn),img的src屬性是一個超長的文件二進制數(shù)據(jù),當(dāng)你需要很多這種的img元素時,建議將展示區(qū)域脫離文檔流,讓其絕對定位減少頁面的 reflow
AJAX 上傳圖片
既然已經(jīng)獲取到拖拽到web頁面中的圖片數(shù)據(jù)了,下一步就是將其發(fā)送到服務(wù)器端。
總結(jié)
- 監(jiān)聽拖拽: 監(jiān)聽頁面元素的拖拽事件,包括: dragenter、dragover、dragleave 和drop,一定要將dragover的默認(rèn)事件取消掉,不然無法觸發(fā)drop事件。如需拖拽頁面里面的元素,需要給其添加屬性draggable="true"
- 獲取拖拽文件: 在 drop 事件觸發(fā)后通過e.dataTransfer.files獲取拖拽文件列表,一定要將drop的默認(rèn)事件取消掉,否則會默認(rèn)打開文件length屬性獲取文件數(shù)量,type屬性獲取文件類型
- 讀取圖片數(shù)據(jù)并添加預(yù)覽圖: 實例化FileReader對象,通過其readAsDataURL(file)方法獲取文件二進制流,并監(jiān)聽其onload事件,將e.result賦值給img的src屬性,最后將圖片添加到DOM中
- 發(fā)送圖片數(shù)據(jù)