jQuery – JavaScript – 教你如何顯示上傳前的多筆預覽圖

* 如果您想要用套件可以來這篇 (2017/03/07更新)
前言可以快速帶過
上一篇 提到預覽圖的製作,使用了 Web APIs 的 FileReader 。後來經過網友的建議與提醒,以及我在實測後發現,一旦使用 FileReader 來讀取檔案,那麼系統資源上會比較吃重,因為瀏覽器會完整讀取你選擇的的每個檔案。所以如果一次選取數十個,瀏覽器就會停擺。網友建議如果只是製作預覽圖,可以改用 URL.createObjectURL() 這個方法。
目標
- 多檔預覽。如 Flickr 上傳圖片
- 結構化程式碼
解決方法
- 使用 vmodel.js 結構化
- 使用 URL.createObjectURL() 這個方法來完成
線上測試
HTML
1 2 3 4 5 6 7 |
<form class="form1"> <input type='file' class="upl" name="upl[]" multiple> <div class="preview"> </div> </form> |
CSS
1 2 3 4 5 6 7 |
.img { max-width: 150px; max-height: 150px; margin: 5px; } |
jQuery/JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// 建立一個模組叫做 preview,根節點為 .form1 $(".form1").vmodel("--preview", true, function (){ var vs = this; // 自動讀取的方法 this.autoload = ['change_file']; // 連續的圖片編碼 this.imgcode = ''; // 選取發生改變 this.change_file = function (){ vs.root.on("change", ".upl", function (){ local_show(this); }); } // 批次圖片,先清空後再插入 var local_show = function (input){ if (input.files && input.files[0]) { local_clean(); local_each_img(input.files); } } // 批次讀取,最後再一次寫入 var local_each_img = function (files){ $.each(files, function (index, file){ console.log(file); //檔案資訊可以在這裡看到 var src = URL.createObjectURL(file); local_create_imgcode(src); }); // 放置預覽元素後重設 vs.root.find(".preview").html(vs.imgcode); local_reset(); } // 建立圖片 var local_create_imgcode = function(src){ vs.imgcode += '<img class="img" src="' + src + '">'; } // 清空預覽區域 var local_clean = function (){ vs.root.find(".preview").empty(); } // 還原 input[type=file] var local_reset = function (){ vs.imgcode = ''; vs.root.find(".upl").val(null); } }); |
解說
一開始有一個參數,是用來後面做為不間斷的 <img><img><img> 存放。
1 2 3 |
this.imgcode = ''; |
接著我們透過 autoload() 可以告訴 vmodel 自動觸發要使用的方法,有 change_file() 這麼一個。
1 2 3 |
this.autoload = ['change_file']; |
首先我們綁定使用者選取圖片的事件,點擊以後會呼叫 local_show(); 並把 input 傳入
1 2 3 4 5 6 7 8 |
// 選取發生改變 this.change_file = function (){ vs.root.on("change", ".upl", function (){ local_show(this); }); } |
當使用者有選取檔案後,才會觸發清空 local_clean() 、批次顯示 local_each_img()
1 2 3 4 5 6 7 8 9 |
// 批次圖片,先清空後再插入 var local_show = function (input){ if (input.files && input.files[0]) { local_clean(); local_each_img(input.files); } } |
清空,主要是把預覽的區域清空,避免不斷選取會不斷堆疊
1 2 3 4 5 6 |
// 清空預覽區域 var local_clean = function (){ vs.root.find(".preview").empty(); } |
因為這次的範例,使用者可以複選圖檔,所以我們使用 $.each 把每張圖片呼叫出來,並利用 URL.createObjectURL() 取得獨特的網址,讓該網址做為預覽圖的路徑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 批次讀取,最後再一次寫入 var local_each_img = function (files){ $.each(files, function (index, file){ console.log(file); //檔案資訊可以在這裡看到 var src = URL.createObjectURL(file); local_create_imgcode(src); }); // 放置預覽元素後重設 vs.root.find(".preview").html(vs.imgcode); local_reset(); } |
其中使用了 local_create_imgcode() 主要是建立圖片編碼,但不直接透過 jQuery 的 append() 或 prepend() 寫入 HTML。這是因為多次插入 DOM 會影響校能,例如有50張圖片那瀏覽器資源不足可能會停擺,所以我們要做的是,在最後一次性的插入DOM。
1 2 3 4 5 6 |
// 建立圖片 var local_create_imgcode = function(src){ vs.imgcode += '<img class="img" src="' + src + '">'; } |
最後 vs.imgcode 會成為一個不間段的標籤字串,我們才在最後加入 DOM,這樣效能才會好。最後我們還原選取鈕供下一次使用。
1 2 3 4 5 6 7 |
// 還原 input[type=file] var local_reset = function (){ vs.imgcode = ''; vs.root.find(".upl").val(null); } |
透過上面 vmodel 結構化 jQuery 與這次的範例目標,整個物件的感覺就更加強烈,維護上也方便許多。關於 vmodel.js 的寫法與文件,可以參考我的 github 或網站 內部相關文章。
那麼如果不使用 vmodel 那要如何組織你的 jQuery 呢?我這邊就以 jQuery 建議與修改,做為組織程式碼的方法做為範例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
var Preview = new function (){ var root = $(".form1"); // 連續的圖片編碼 var imgcode = ''; // 選取發生改變 this.change_file = function (){ root.on("change", ".upl", function (){ show(this); }); } // 批次圖片,先清空後再插入 var show = function (input){ if (input.files && input.files[0]) { clean(); each_img(input.files); } } // 批次讀取,最後再一次寫入 var each_img = function (files){ $.each(files, function (index, file){ var src = URL.createObjectURL(file); create_imgcode(src); }); // 放置預覽元素後重設 root.find(".preview").html(imgcode); reset(); } // 建立圖片 var create_imgcode = function(src){ imgcode += '<img class="img" src="' + src + '">'; } // 清空預覽區域 var clean = function (){ root.find(".preview").empty(); } // 還原 input[type=file] var reset = function (){ imgcode = ''; root.find(".upl").val(null); } } // 執行 Preview.change_file(); |
當然相較之下,會是使用 vmodel 的可塑性高出許多囉!
吳中正
2016-04-21 - 22:33
您好,
讀完本文,有問題無法理解,
問題如下:
1)
前端使用上預覽圖片沒有問題,但是….
似乎因為第38行執行local_reset()後,要上傳的資料被清掉了
如:http://www.chit.com.tw/zSelect.asp
2)
如果不執行local_reset(),註解掉以後
若重新選擇圖片,圖片會一直堆疊無法清除
按了reset鍵,input會清除,重選圖片後圖片依然會繼續增加
找多圖片上傳前預覽的方法有好多天了
您的方法是其中最喜愛的
若您時間允許,是不可以協助指點呢?
打擾您了~~
萬分感謝~~
jsn
2016-05-26 - 12:23
最近很少更新這裡現在才發現好多人留言XD
基本上這邊介紹的就是基礎應用。如果要搭配上傳,只要稍作變化就可以了,我自己連接後端上傳沒有問題喔~
可以再思考一下如何改變。local_reset()是必要的喔,不使用會堆疊是正常的。
謝謝你的認同,很感謝你~~~
吳中正
2016-05-26 - 13:23
謝謝您
我沒有想到解決辦法
最後加了一個按鈕
請User按那個按鈕將頁面reload重新選擇圖片^^
自己該用心學習jQuery了
再次感謝~~
jsn
2016-06-07 - 16:40
你是說顯示區域的圖片堆疊嗎?突然意會~
如果是的話,使用 $(selector).html(”); 看看
賴韻婷
2017-01-11 - 18:26
不好意思,想請問”未選擇任何檔案”是否有方法可以移除?
若您方便回答,再請您幫忙了,謝謝!:)
JSN
2017-03-02 - 09:43
通常還是不建議直接秀出 因為太醜了XD
建議可以包裝精美一點,也可避免顯示未選擇任何檔案,例如
簡單寫的範例
Gabby
2017-02-15 - 14:45
請問~如果我對每張圖做一個小叉叉,點了可以刪除單一圖片,這樣要怎麼寫比較好呢@@?
我用jquery做click沒有效果…想請原po指點一下~感謝~
JSN
2017-03-02 - 09:48
這個算蠻基本的呢,基礎要再反覆練習一下。Google 搜尋 jquery 的 on() 相關教學試試
Majiko
2017-06-09 - 13:49
您好~看到原po的方法覺得很實用
不過目前套用在本人專案上時卻發生想不通的問題
我是使用您下面非vmodel的方法
執行到 // 批次圖片,先清空後再插入 這段程式碼時就沒有執行成功了
圖片的預覽也沒有出現 卡好久還是想不通
想請教這會是什麼問題呢?感謝~
JSN
2017-06-11 - 15:36
嗯…這樣的話我也不是很懂,有相關的程式碼片段嗎?
Majiko
2017-06-12 - 08:40
早安你好,以下為我的程式碼片段
在JS的部分段落中我有加入alert()函數來判斷有沒有執行到
不過卻只有在進入此頁面時跳出alert(‘change’)
選取檔案後除了有顯示選擇的檔案名稱 圖片預覽並未出現
會是我撰寫的部分有錯誤或是少了什麼嗎?
(不好意思有點長,有空方便回答的話,再請您幫忙了,謝謝!)
————————————-
//連續顯示上傳圖片
var Preview = new function (){
var root = $(“.text-center”);
// 連續的圖片編碼
var imgcode = ”;
// 選取發生改變
this.change_file = function (){
root.on(“change”, “.userfile”, function (){
show(this);
alert(this);
});
alert(‘change’);
}
// 批次圖片,先清空後再插入
var show = function (input){
alert(‘show’);
if (input.files && input.files[0]) {
//clean();
alert(‘show2’)
each_img(input.files);
}
}
// 批次讀取,最後再一次寫入
var each_img = function (files){
$.each(files, function (index, file){
var src = URL.createObjectURL(file);
alert(src);
create_imgcode(src);
});
// 放置預覽元素後重設
root.find(“.preview”).html(imgcode);
alert(‘put’);
//reset();
}
// 建立圖片
var create_imgcode = function(src){
imgcode += ”;
alert(imgcode);
}
}
// 執行
Preview.change_file();
———————————————–
安安
2018-06-07 - 13:11
不好意思 請問這種方法可以再加以寫入PHP 的BLOB類型嗎
JSN
2018-06-11 - 09:32
你想要的是 PHP 透過 Blob 讀取伺服器資料夾的圖片嗎?我想網路有很多資源你先找找,等我有時間再寫。