開發中會經常涉及到文件上傳的需求,根據業務不同的需求,有不同的文件上傳情況。 有簡單的單文件上傳,有多文件上傳,因瀏覽器原生的文件上傳樣式及功能的支持度不算太高,很多時候我們會對樣式進行美化,對功能進行完善。 本文根據一個例子,對多文件的上傳樣式做了一些簡單的美化(其實也沒怎麼美化。。),同時支持選 ...
開發中會經常涉及到文件上傳的需求,根據業務不同的需求,有不同的文件上傳情況。
有簡單的單文件上傳,有多文件上傳,因瀏覽器原生的文件上傳樣式及功能的支持度不算太高,很多時候我們會對樣式進行美化,對功能進行完善。
本文根據一個例子,對多文件的上傳樣式做了一些簡單的美化(其實也沒怎麼美化。。),同時支持選擇文件後自定義刪除相關的文件,最後再上傳
文章篇幅較長,先簡單看看圖示:
目錄
一、文件上傳基礎
1. 單文件上傳
最簡單的文件上傳,是單文件上傳,form標簽中加入enctype="multipart/form-data",form表單中有一個input[type="file"]項
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data"> <input type="text" name="user" id="user" placeholder="請輸入昵稱"> <input type="file" name="userImage" id="userImage"> <input type="submit" name="sub" value="提交"> </form>
2. 多文件上傳
1)類似單文件上傳,簡單的多文件上傳其實就是多幾個input[type="file"]項
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data"> <input type="text" name="user" id="user" placeholder="請輸入昵稱"> <input type="file" name="userImage1" id="userImage1"> <input type="file" name="userImage2" id="userImage2"> <input type="file" name="userImage3" id="userImage3"> <input type="submit" name="sub" value="提交"> </form>
2) HTML5為表單文件項新增了一個multiple屬性,可以設置實現選擇多個文件,如
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data"> <input type="text" name="user" id="user" placeholder="請輸入昵稱"> <input type="file" name="userImage" id="userImage" multiple> <input type="submit" name="sub" value="提交"> </form>
要註意的是,對於multiple這個新屬性,在IE9及以下版本中不被支持,在移動端安卓平臺下會忽略,也就是只能選擇一個文件
二、表單文件上傳的美化
看了上面幾個圖片,可以知道原生的文件選擇項樣式是最基本的,主要體現在三個點:
- 無邊框,與其他有邊框的元素不合拍
- 選擇文件的按鈕樣式太基礎
- 選擇多個文件後只顯示總數,未顯示詳細選擇的文件名
基於幾個問題,可以按需對其進行美化
第一點可以直接添加邊框的樣式
第二點需要增添其他元素,可以新增一個按鈕(自行按需美化),將原始文件框隱藏,用JS事件綁定,點擊按鈕後模擬文件框的點擊
<input type="file" name="userImage" id="userImage" style="display: none;"> <input type="button" id="" value="選擇文件" onclick="document.getElementById('userImage').click()">
第三點與第二點類似,也得添加新的元素,選擇文件後,通過JS獲取選擇的文件信息,併在新的元素中顯示出來
想著很簡單,但隨之而來的問題就是,如果選中的文件數量很多,新元素占空間的多少就是個問題,可以預設顯示幾個文件,再通過“查看更多文件”查看到更多的信息
隨之另外的想法是,一次性選中的文件很多,想取消某個文件時,又得重新選擇。這未免太繁瑣,所以需要提供即時刪除某個選中文件的操作
三、選中文件後的刪除
要提供選中文件後可刪除的操作,就必然需要提供相關入口及腳本操作,下麵圍繞這點來做些解析
1. 界面的處理
選擇文件後,我們可以通過刪除按鈕刪除選中的文件,因為會出現多文件的情況,所以需要一個信息模版
<!-- 當前選擇的文件列表 文件信息模版 --> <script type="text/template" id="file-temp-item-tpl"> <span class="file-temp-item" style="{{style}}"> <span class="file-temp-name">{{name}}</span> <span class="file-temp-btn">×</span> </span> </script>
選中的文件一多,就得再增添一個下拉框做輔助,最多顯示5個文件信息,然後通過下拉按鈕展開下拉框(按鈕樣式自行設定)
這裡5個文件間的位置計算的不是很到位,主要是這段代碼,可以自行設定
// 計算每一項坐標left、占寬width left = i === 0 ? 2 : 2 + i * (100 / fileTempLen); width = 100 / fileTempLen - 2;
下拉列表裡面的每一項也是一個模版
<!-- 查看更多文件 文件信息模版 --> <script type="text/template" id="file-more-item-tpl"> <li> <span class="file-item-more-name">{{name}}</span> <span class="file-item-more-btn">×</span> </li> </script>
以下為初始的HTML結構
<form name="form" id="form" method="post" action="fileTest.php" enctype="multipart/form-data"> <!-- <input type="number" name="numberTest" value="100"> --> <input type="file" name="fileTest[]" id="fileTest" multiple> <!-- 當前選擇的文件列表(最多顯示5條) --> <span class="file-temp"> </span> <!-- 查看更多文件 --> <ul class="item-more"> </ul> <input type="button" class="btn btn-success" id="uploadBtn" value="上傳"> <p class="upload-tip">文件上傳成功</p> </form>
以下為全部CSS樣式
1 <link rel="stylesheet" type="text/css" href="bootstrap.min.css"> 2 <style type="text/css"> 3 html { 4 font-family: Arial; 5 } 6 7 form { 8 margin: 50px auto; 9 width: 400px; 10 } 11 12 input { 13 width: 300px; 14 padding: 4px; 15 } 16 17 #uploadBtn { 18 margin-top: -3px; 19 margin-left: 5px; 20 width: 60px; 21 height: 30px; 22 font-weight: bold; 23 font-size: 12px; 24 } 25 26 #fileTest { 27 display: inline-block; 28 border: 1px solid #ccc; 29 border-radius: 3px; 30 } 31 32 .file-temp { 33 position: relative; 34 display: none; 35 width: 300px; 36 height: 31px; 37 } 38 39 .file-temp-item { 40 position: absolute; 41 top: 4px; 42 height: 24px; 43 } 44 45 .item-more-btn { 46 display: inline-block; 47 position: absolute; 48 top: 18px; 49 right: 0.5%; 50 width: 10px; 51 height: 10px; 52 color: #777; 53 cursor: pointer; 54 } 55 56 .item-more-btn:hover { 57 border-top-color: #aaa; 58 } 59 60 .file-temp-name { 61 display: inline-block; 62 overflow: hidden; 63 width: 90%; 64 height: 26px; 65 padding: 2px 15px 2px 5px; 66 border-radius: 2px; 67 background-color: #eaeaf3; 68 text-overflow: ellipsis; 69 white-space: nowrap; 70 } 71 .file-temp-btn { 72 position: absolute; 73 display: inline-block; 74 top: 4px; 75 right: 11%; 76 width: 18px; 77 height: 18px; 78 line-height: 18px; 79 text-align: center; 80 border: 1px solid #ddd; 81 background-color: #ccc; 82 border-radius: 50%; 83 color: #fff; 84 font-size: 18px; 85 cursor: pointer; 86 } 87 88 .item-more { 89 position: absolute; 90 overflow-y: auto; 91 display: none; 92 padding-left: 0; 93 width: 300px; 94 max-height: 150px; 95 list-style: none; 96 } 97 98 .item-more li { 99 position: relative; 100 padding: 5px; 101 border: 1px solid #ccc; 102 border-top: none; 103 } 104 .item-more li:hover { 105 background-color: #f5f5f9; 106 } 107 108 .file-item-more-name { 109 display: inline-block; 110 width: 90%; 111 overflow: hidden; 112 text-overflow: ellipsis; 113 white-space: nowrap; 114 } 115 .file-item-more-btn { 116 position: absolute; 117 display: inline-block; 118 top: 8px; 119 right: 2%; 120 width: 18px; 121 height: 18px; 122 line-height: 18px; 123 text-align: center; 124 border: 1px solid #ddd; 125 background-color: #ddd; 126 border-radius: 50%; 127 color: #fff; 128 font-size: 18px; 129 cursor: pointer; 130 } 131 .file-item-more-btn:hover { 132 background-color: #ccc; 133 } 134 135 .upload-tip { 136 display: none; 137 margin: 50px auto; 138 text-align: center; 139 font-size: 12px; 140 } 141 </style>View Code
2. 腳本的處理
下麵,著重介紹JS腳本的處理
要獲取到選中文件的信息,自然想到用value屬性,但通過文件項的value只能獲取到一個文件路徑(第一個),無論有沒有multiple
無multiple
<input type="file" onchange="console.log(this.value);">
有multiple
<input type="file" multiple onchange="console.log(this.value);">
既然直接通過value獲取不到所有選中的文件信息,只能尋求其他途徑。
獲取選中的文件信息,還可以用FileList對象,這是在HTML5中新增的,每個表單文件項都有個files屬性,裡邊存儲這選中的文件的一些信息
<input type="file" multiple onchange="console.log(this.files);">
選中兩個文件後,查看文件信息
FileList對象看起來是個類數組,有length屬性。所以我們應該可以通過修改或刪除相關的項來自定義我們選擇的文件(註意其實這是不能修改的,且繼續看下去)
假如我選擇了兩個文件,想刪除第二項目,使用splice刪除,則
<input type="file" multiple onchange="console.log(Array.prototype.splice.call(this.files, 1, 1));">
報錯,由此可知FileList的length屬性是只讀的,那直接修改為可寫可配置呢
Object.defineProperty(FileList.prototype, 'length', { writable: true, configurable: true });
配置之後length能修改了,乍一看還以為splice生效了,然而輸出一看,FileList對象內容不變,仍為兩項
查閱了一些資料後,瞭解到瀏覽器為了安全性的考慮,把FileList對象的內容設為了不可更改,只可以手動置空,但不能修改內容
所以,解決辦法是,新增一個數組,初始複製FileList對象的文件內容,之後的修改操作則通過這個可更改的數組進行