JS實(shí)現(xiàn)表單多文件上傳樣式美化支持選中文件后刪除相關(guān)項(xiàng)
開(kāi)發(fā)中會(huì)經(jīng)常涉及到文件上傳的需求,根據(jù)業(yè)務(wù)不同的需求,有不同的文件上傳情況。
有簡(jiǎn)單的單文件上傳,有多文件上傳,因?yàn)g覽器原生的文件上傳樣式及功能的支持度不算太高,很多時(shí)候我們會(huì)對(duì)樣式進(jìn)行美化,對(duì)功能進(jìn)行完善。
本文根據(jù)一個(gè)例子,對(duì)多文件的上傳樣式做了一些簡(jiǎn)單的美化(其實(shí)也沒(méi)怎么美化。。),同時(shí)支持選擇文件后自定義刪除相關(guān)的文件,最后再上傳
文章篇幅較長(zhǎng),先簡(jiǎn)單看看圖示:
一、文件上傳基礎(chǔ)
1. 單文件上傳
最簡(jiǎn)單的文件上傳,是單文件上傳,form標(biāo)簽中加入enctype="multipart/form-data",form表單中有一個(gè)input[type="file"]項(xiàng)
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data"> <input type="text" name="user" id="user" placeholder="請(qǐng)輸入昵稱"> <input type="file" name="userImage" id="userImage"> <input type="submit" name="sub" value="提交"> </form>
2. 多文件上傳
1)類似單文件上傳,簡(jiǎn)單的多文件上傳其實(shí)就是多幾個(gè)input[type="file"]項(xiàng)
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data"> <input type="text" name="user" id="user" placeholder="請(qǐng)輸入昵稱"> <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為表單文件項(xiàng)新增了一個(gè)multiple屬性,可以設(shè)置實(shí)現(xiàn)選擇多個(gè)文件,如
<form name="form1" method="post" action="/abc.php" enctype="multipart/form-data">
<input type="text" name="user" id="user" placeholder="請(qǐng)輸入昵稱">
<input type="file" name="userImage" id="userImage" multiple>
<input type="submit" name="sub" value="提交">
</form>
二、表單文件上傳的美化
看了上面幾個(gè)圖片,可以知道原生的文件選擇項(xiàng)樣式是最基本的,主要體現(xiàn)在三個(gè)點(diǎn):
無(wú)邊框,與其他有邊框的元素不合拍
選擇文件的按鈕樣式太基礎(chǔ)
選擇多個(gè)文件后只顯示總數(shù),未顯示詳細(xì)選擇的文件名
基于幾個(gè)問(wèn)題,可以按需對(duì)其進(jìn)行美化
第一點(diǎn)可以直接添加邊框的樣式
第二點(diǎn)需要增添其他元素,可以新增一個(gè)按鈕(自行按需美化),將原始文件框隱藏,用JS事件綁定,點(diǎn)擊按鈕后模擬文件框的點(diǎn)擊
<input type="file" name="userImage" id="userImage" style="display: none;"> <input type="button" id="" value="選擇文件" onclick="document.getElementById('userImage').click()">
第三點(diǎn)與第二點(diǎn)類似,也得添加新的元素,選擇文件后,通過(guò)JS獲取選擇的文件信息,并在新的元素中顯示出來(lái)
想著很簡(jiǎn)單,但隨之而來(lái)的問(wèn)題就是,如果選中的文件數(shù)量很多,新元素占空間的多少就是個(gè)問(wèn)題,可以默認(rèn)顯示幾個(gè)文件,再通過(guò)“查看更多文件”查看到更多的信息
隨之另外的想法是,一次性選中的文件很多,想取消某個(gè)文件時(shí),又得重新選擇。這未免太繁瑣,所以需要提供即時(shí)刪除某個(gè)選中文件的操作
三、選中文件后的刪除
要提供選中文件后可刪除的操作,就必然需要提供相關(guān)入口及腳本操作,下面圍繞這點(diǎn)來(lái)做些解析
1. 界面的處理
選擇文件后,我們可以通過(guò)刪除按鈕刪除選中的文件,因?yàn)闀?huì)出現(xiàn)多文件的情況,所以需要一個(gè)信息模版
<!-- 當(dāng)前選擇的文件列表 文件信息模版 --> <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>
選中的文件一多,就得再增添一個(gè)下拉框做輔助,最多顯示5個(gè)文件信息,然后通過(guò)下拉按鈕展開(kāi)下拉框(按鈕樣式自行設(shè)定)
這里5個(gè)文件間的位置計(jì)算的不是很到位,主要是這段代碼,可以自行設(shè)定
// 計(jì)算每一項(xiàng)坐標(biāo)left、占寬width left = i === 0 ? 2 : 2 + i * (100 / fileTempLen); width = 100 / fileTempLen - 2;
下拉列表里面的每一項(xiàng)也是一個(gè)模版
<!-- 查看更多文件 文件信息模版 --> <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結(jié)構(gòu)
<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> <!-- 當(dāng)前選擇的文件列表(最多顯示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樣式
<link rel="stylesheet" type="text/css" href="bootstrap.min.css"> <style type="text/css"> html { font-family: Arial; } form { margin: 50px auto; width: 400px; } input { width: 300px; padding: 4px; } #uploadBtn { margin-top: -3px; margin-left: 5px; width: 60px; height: 30px; font-weight: bold; font-size: 12px; } #fileTest { display: inline-block; border: 1px solid #ccc; border-radius: 3px; } .file-temp { position: relative; display: none; width: 300px; height: 31px; } .file-temp-item { position: absolute; top: 4px; height: 24px; } .item-more-btn { display: inline-block; position: absolute; top: 18px; right: 0.5%; width: 10px; height: 10px; color: #777; cursor: pointer; } .item-more-btn:hover { border-top-color: #aaa; } .file-temp-name { display: inline-block; overflow: hidden; width: 90%; height: 26px; padding: 2px 15px 2px 5px; border-radius: 2px; background-color: #eaeaf3; text-overflow: ellipsis; white-space: nowrap; } .file-temp-btn { position: absolute; display: inline-block; top: 4px; right: 11%; width: 18px; height: 18px; line-height: 18px; text-align: center; border: 1px solid #ddd; background-color: #ccc; border-radius: 50%; color: #fff; font-size: 18px; cursor: pointer; } .item-more { position: absolute; overflow-y: auto; display: none; padding-left: 0; width: 300px; max-height: 150px; list-style: none; } .item-more li { position: relative; padding: 5px; border: 1px solid #ccc; border-top: none; } .item-more li:hover { background-color: #f5f5f9; } .file-item-more-name { display: inline-block; width: 90%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .file-item-more-btn { position: absolute; display: inline-block; top: 8px; right: 2%; width: 18px; height: 18px; line-height: 18px; text-align: center; border: 1px solid #ddd; background-color: #ddd; border-radius: 50%; color: #fff; font-size: 18px; cursor: pointer; } .file-item-more-btn:hover { background-color: #ccc; } .upload-tip { display: none; margin: 50px auto; text-align: center; font-size: 12px; } </style>
2. 腳本的處理
下面,著重介紹JS腳本的處理
要獲取到選中文件的信息,自然想到用value屬性,但通過(guò)文件項(xiàng)的value只能獲取到一個(gè)文件路徑(第一個(gè)),無(wú)論有沒(méi)有multiple
無(wú)multiple
<input type="file" onchange="console.log(this.value);">
有multiple
<input type="file" multiple onchange="console.log(this.value);">
既然直接通過(guò)value獲取不到所有選中的文件信息,只能尋求其他途徑。
1)FileList
獲取選中的文件信息,還可以用FileList對(duì)象,這是在HTML5中新增的,每個(gè)表單文件項(xiàng)都有個(gè)files屬性,里邊存儲(chǔ)這選中的文件的一些信息
<input type="file" multiple onchange="console.log(this.files);">
選中兩個(gè)文件后,查看文件信息
FileList對(duì)象看起來(lái)是個(gè)類數(shù)組,有l(wèi)ength屬性。所以我們應(yīng)該可以通過(guò)修改或刪除相關(guān)的項(xiàng)來(lái)自定義我們選擇的文件(注意其實(shí)這是不能修改的,且繼續(xù)看下去)
假如我選擇了兩個(gè)文件,想刪除第二項(xiàng)目,使用splice刪除,則
<input type="file" multiple onchange="console.log(Array.prototype.splice.call(this.files, 1, 1));">
報(bào)錯(cuò),由此可知FileList的length屬性是只讀的,那直接修改為可寫(xiě)可配置呢
Object.defineProperty(FileList.prototype, 'length', { writable: true, configurable: true });
配置之后length能修改了,乍一看還以為splice生效了,然而輸出一看,F(xiàn)ileList對(duì)象內(nèi)容不變,仍為兩項(xiàng)
查閱了一些資料后,了解到瀏覽器為了安全性的考慮,把FileList對(duì)象的內(nèi)容設(shè)為了不可更改,只可以手動(dòng)置空,但不能修改內(nèi)容
所以,解決辦法是,新增一個(gè)數(shù)組,初始復(fù)制FileList對(duì)象的文件內(nèi)容,之后的修改操作則通過(guò)這個(gè)可更改的數(shù)組進(jìn)行
// 存儲(chǔ)更新所選文件 var curFiles = []; ... // 選中文件后 var files = this.files; if (files && files.length) { // 原始FileList對(duì)象不可更改,所以將其賦予curFiles提供接下來(lái)的修改 Array.prototype.push.apply(curFiles, files); }
假如點(diǎn)擊了刪除叉叉,可以直接更新文件信息數(shù)組
var name = $(this).prev().text(); // 去除該文件 curFiles = curFiles.filter(function(file) { return file.name !== name; });
這樣一來(lái),更新文件信息的問(wèn)題得到解決,然后就可以進(jìn)行文件的上傳了
點(diǎn)擊文件上傳,如果直接調(diào)用$form.submit(); 則上傳的文件信息依然是初始的FileList對(duì)象,達(dá)不到我們自定義的要求,所以需要用Ajax提交
那么,該怎么想后臺(tái)提供一個(gè)文件對(duì)象呢?
2)FormData
HTML5引入了表單的新對(duì)象FormData, 它可以生成一個(gè)表單對(duì)象,我們可以向其中獲取/設(shè)置鍵值對(duì)信息,再一并提交給后臺(tái)
引用MDN的FormData使用方法,我們可以添加各種類型的數(shù)據(jù),使用ajax提交
var oMyForm = new FormData(); oMyForm.append("username", "Groucho"); oMyForm.append("accountnum", 123456); // 數(shù)字123456被立即轉(zhuǎn)換成字符串"123456" // fileInputElement中已經(jīng)包含了用戶所選擇的文件 oMyForm.append("userfile", fileInputElement.files[0]); var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // Blob對(duì)象包含的文件內(nèi)容 var oBlob = new Blob([oFileBody], { type: "text/xml"}); oMyForm.append("webmasterfile", oBlob); var oReq = new XMLHttpRequest(); oReq.open("POST", "http://foo.com/submitform.php"); oReq.send(oMyForm);
也可使用JQ的封裝的ajax,不過(guò)要注意設(shè)置processData和contentType屬性為false,防止JQ胡亂解析文件格式
var fd = new FormData(document.getElementById("fileinfo")); // 使用某個(gè)表單作為初始項(xiàng) fd.append("CustomField", "This is some extra data"); $.ajax({ url: "stash.php", type: "POST", data: fd, processData: false, // 告訴jQuery不要去處理發(fā)送的數(shù)據(jù) contentType: false // 告訴jQuery不要去設(shè)置Content-Type請(qǐng)求頭 });
這里有幾個(gè)要注意的點(diǎn):
1)FormData中的屬性值接受的是單個(gè)文件信息,不能是復(fù)合性的對(duì)象??赡鼙硪獠幻?,且看
var fd = new FormData($('#form')[0]); fd.append('myFileTest', curFiles); $files = $_REQUEST['myFileTest']; var_dump($files);
用PHP接收傳過(guò)來(lái)的數(shù)據(jù),數(shù)據(jù)卻被直接轉(zhuǎn)換成字符串了,非文件對(duì)象
curFiles是文件對(duì)象,那PHP端是不是應(yīng)該用$_FILES來(lái)接收信息呢,試試換成$files = $_FILES['myFileTest'];
直接出問(wèn)題了,說(shuō)明不能這樣處理,需要將curFiles內(nèi)容一項(xiàng)一項(xiàng)拆開(kāi),即單個(gè)文件信息
var fd = new FormData($('#form')[0]); for (var i = 0, j = curFiles.length; i < j; ++i) { fd.append('myFileTest[]', curFiles[i]); } $files = $_FILES['myFileTest']; var_dump($files);
文件接收成功,接下來(lái)就可以按需進(jìn)行文件的操作了
2)后端獲取文件信息的時(shí)候,是直接通過(guò)原始$_FILES獲取的,其他一般的信息才用$_REQUEST獲取
換成$files = $_REQUEST['myFileTest'];試試,直接就是出現(xiàn)找不到myFileTest的問(wèn)題
試試添加一般的文件再提交
var fd = new FormData($('#form')[0]); for (var i = 0, j = curFiles.length; i < j; ++i) { fd.append('myFileTest[]', curFiles[i]); } fd.append('myTest', [1, 2, 3]); $files = $_FILES['myFileTest']; $test = $_REQUEST['myTest']; var_dump($test); var_dump($files);
3)如果需要multiple的多文件上傳,則需要在文件項(xiàng)的文件后添加[]號(hào),表示這是一個(gè)多文件的數(shù)組,以供后端處理解析
fd.append('myFileTest[]', curFiles[i]);
如果沒(méi)有后面的[],則連續(xù)的append會(huì)直接覆蓋原來(lái)的,最后后端獲取到的只是最后append進(jìn)去的項(xiàng)
4)不要直接在JQ的ajax中實(shí)例化出一個(gè)FormData對(duì)象,會(huì)出問(wèn)題
直接在data屬性中生成FormData對(duì)象,會(huì)被JQ忽略,所以后端什么信息也拿不到
混合表單項(xiàng)簡(jiǎn)單的例子:
在表單處理中,很多時(shí)候我們會(huì)進(jìn)行文件上傳和其他基礎(chǔ)項(xiàng)的提交,簡(jiǎn)單地多加一個(gè)input項(xiàng)目,看看是否處理成功
<input type="number" name="numberTest" value="100">
<?php $files = $_FILES['myFileTest']; $test = $_REQUEST['numberTest']; echo json_encode(array( 'len' => count($files['name']), 'num' => $test )); ?>
以下為全部的JS腳本:
<script type="text/javascript"> /** * 向文件列表元素中添加相應(yīng)的文件項(xiàng) * @param {Array} files 當(dāng)前的文件列表數(shù)組對(duì)象 */ function addItem(files) { var fileTempItemTpl = $('#file-temp-item-tpl').html(), fileMoreItemTpl = $('#file-more-item-tpl').html() htmlTemp = [], htmlMoreTemp = [], // 文件列表中各文件坐標(biāo)位置及所占空間 left = 2, width = 100, // 最多取前5個(gè)文件 fileTempLen = files.length > 5 ? 5 : files.length; for (var i = 0, j = files.length; i < j; ++i) { // 當(dāng)i > 4,即第6個(gè)文件開(kāi)始 if (i > 4) { htmlMoreTemp.push(fileMoreItemTpl.replace('{{name}}', files[i].name)); continue; } // 計(jì)算每一項(xiàng)坐標(biāo)left、占寬width left = i === 0 ? 2 : 2 + i * (100 / fileTempLen); width = 100 / fileTempLen - 2; htmlTemp.push(fileTempItemTpl .replace('{{style}}', 'left: ' + left + '%;width: ' + width + '%;') .replace('{{name}}', files[i].name) ); } // 渲染相關(guān)元素內(nèi)容 $('.file-temp').html('' + '<input type="text" style="background-color:#fff;" class="form-control" id="fileTemp" readonly>' + htmlTemp.join('') + (files.length > 5 ? '<span class="item-more-btn" title="查看更多">=</span>' : '' ) ); $('.item-more').html(htmlMoreTemp.join('')); } // 保存當(dāng)前選擇的(更新后)文件列表 var curFiles = []; // 初始選擇文件時(shí)觸發(fā) $('#fileTest').change(function() { var $this = $(this), $temp = $('.file-temp'), files = this.files; if (files && files.length) { // 原始FileList對(duì)象不可更改,所以將其賦予curFiles提供接下來(lái)的修改 Array.prototype.push.apply(curFiles, files); addItem(curFiles); $this.hide(); $temp.css('display', 'inline-block'); } }); $(document) // 取消選擇某個(gè)文件時(shí),在文件列表數(shù)組對(duì)象中刪除這個(gè)值,并更新列表 .on('click', '.file-temp-btn, .file-item-more-btn', function() { $('.upload-tip').hide(); var name = $(this).prev().text(); // 去除該文件 curFiles = curFiles.filter(function(file) { return file.name !== name; }); // 文件列表數(shù)組對(duì)象長(zhǎng)度大于5才顯示“更多文件列表”下拉項(xiàng) if (curFiles.length <= 5) { $('.item-more').hide(); } // 文件列表數(shù)組被清空則重置文件選擇表單項(xiàng) if (!curFiles.length) { $('#fileTest').val('').show(); $('.file-temp').css('display', 'none'); } else { addItem(curFiles); } console.log(curFiles) }) // 顯示“更多文件列表”下拉項(xiàng) .on('click', '.item-more-btn', function() { $('.upload-tip').hide(); $('.item-more').show('normal'); }); // 上傳操作 $('#uploadBtn').click(function() { $('.upload-tip').hide(); // 構(gòu)建FormData對(duì)象 var fd = new FormData($('#form')[0]); for (var i = 0, j = curFiles.length; i < j; ++i) { fd.append('myFileTest[]', curFiles[i]); } $.ajax({ url: 'fileTest.php', type: 'post', data: fd, processData: false, contentType: false, success: function(rs) { rs = JSON.parse(rs); $('.upload-tip') .addClass('text-success') .removeClass('text-error') .text(rs.len + '個(gè)文件上傳成功, number項(xiàng)值為' + rs.num) .show(); }, error: function(err) { } }); }); </script>
以上所述是小編給大家介紹的JS實(shí)現(xiàn)表單多文件上傳樣式美化支持選中文件后刪除相關(guān)項(xiàng),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- PHP中上傳多個(gè)文件的表單設(shè)計(jì)例子
- Asp.Net模擬表單提交數(shù)據(jù)和上傳文件的實(shí)現(xiàn)代碼
- nodejs 實(shí)現(xiàn)模擬form表單上傳文件
- Ajax方式提交帶文件上傳的表單及隱藏iframe應(yīng)用
- php中關(guān)于普通表單多文件上傳的處理方法
- php文件上傳表單摘自drupal的代碼
- ajax(iframe)無(wú)刷新提交表單、上傳文件
- asp.net 模擬提交有文件上傳的表單(通過(guò)http模擬上傳文件)
- 通過(guò)表單的做為二進(jìn)制文件上傳request.totalbytes提取出上傳的二級(jí)制數(shù)據(jù)
相關(guān)文章
JavaScript實(shí)現(xiàn)獲取img的原始尺寸的方法詳解
在微信小程序開(kāi)發(fā)時(shí),它的image標(biāo)簽有一個(gè)默認(rèn)高度,這樣你的圖片很可能出現(xiàn)被壓縮變形的情況,所以就需要獲取到圖片的原始尺寸對(duì)image的寬高設(shè)置,本文就來(lái)分享一下JavaScript實(shí)現(xiàn)獲取img的原始尺寸的方法吧2023-03-03理解Javascript的動(dòng)態(tài)語(yǔ)言特性
這篇文章主要介紹了理解Javascript的動(dòng)態(tài)語(yǔ)言特性,需要的朋友可以參考下2015-06-06JavaScript中獲取高度和寬度函數(shù)總結(jié)
這篇文章主要介紹了JavaScript中獲取高度和寬度函數(shù)總結(jié),例如獲取視窗大小、可見(jiàn)區(qū)域?qū)?、可?jiàn)區(qū)域高、獲取元素自身大小等,很方便的一個(gè)總結(jié),需要的朋友可以參考下2014-10-10jQuery實(shí)現(xiàn)可收縮展開(kāi)的級(jí)聯(lián)菜單實(shí)例代碼
這篇文章主要是對(duì)利用jQuery實(shí)現(xiàn)可收縮展開(kāi)的級(jí)聯(lián)菜單的實(shí)例代碼進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11微信小程序?qū)崿F(xiàn)左滑動(dòng)刪除效果
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)左滑動(dòng)刪除效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07靈活使用數(shù)組制作圖片切換js實(shí)現(xiàn)
這篇文章主要介紹了靈活使用數(shù)組制作圖片切換效果,js實(shí)現(xiàn)圖片切換特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07