JS实现表单多文件上传样式美化支持选中文件后删除相关项


Posted in Javascript onSeptember 30, 2016

开发中会经常涉及到文件上传的需求,根据业务不同的需求,有不同的文件上传情况。

有简单的单文件上传,有多文件上传,因浏览器原生的文件上传样式及功能的支持度不算太高,很多时候我们会对样式进行美化,对功能进行完善。

本文根据一个例子,对多文件的上传样式做了一些简单的美化(其实也没怎么美化。。),同时支持选择文件后自定义删除相关的文件,最后再上传

文章篇幅较长,先简单看看图示:

JS实现表单多文件上传样式美化支持选中文件后删除相关项

一、文件上传基础

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>

JS实现表单多文件上传样式美化支持选中文件后删除相关项

二、表单文件上传的美化

看了上面几个图片,可以知道原生的文件选择项样式是最基本的,主要体现在三个点:

无边框,与其他有边框的元素不合拍
选择文件的按钮样式太基础
选择多个文件后只显示总数,未显示详细选择的文件名
基于几个问题,可以按需对其进行美化

第一点可以直接添加边框的样式

第二点需要增添其他元素,可以新增一个按钮(自行按需美化),将原始文件框隐藏,用JS事件绑定,点击按钮后模拟文件框的点击

<input type="file" name="userImage" id="userImage" style="display: none;">
<input type="button" id="" value="选择文件" onclick="document.getElementById('userImage').click()">

第三点与第二点类似,也得添加新的元素,选择文件后,通过JS获取选择的文件信息,并在新的元素中显示出来

想着很简单,但随之而来的问题就是,如果选中的文件数量很多,新元素占空间的多少就是个问题,可以默认显示几个文件,再通过“查看更多文件”查看到更多的信息

随之另外的想法是,一次性选中的文件很多,想取消某个文件时,又得重新选择。这未免太繁琐,所以需要提供即时删除某个选中文件的操作

三、选中文件后的删除

要提供选中文件后可删除的操作,就必然需要提供相关入口及脚本操作,下面围绕这点来做些解析

1. 界面的处理

JS实现表单多文件上传样式美化支持选中文件后删除相关项JS实现表单多文件上传样式美化支持选中文件后删除相关项

选择文件后,我们可以通过删除按钮删除选中的文件,因为会出现多文件的情况,所以需要一个信息模版

<!-- 当前选择的文件列表 文件信息模版 -->
<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个文件信息,然后通过下拉按钮展开下拉框(按钮样式自行设定)

JS实现表单多文件上传样式美化支持选中文件后删除相关项

这里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样式

<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属性,但通过文件项的value只能获取到一个文件路径(第一个),无论有没有multiple

无multiple

<input type="file" onchange="console.log(this.value);">

JS实现表单多文件上传样式美化支持选中文件后删除相关项

有multiple

<input type="file" multiple onchange="console.log(this.value);">

JS实现表单多文件上传样式美化支持选中文件后删除相关项

既然直接通过value获取不到所有选中的文件信息,只能寻求其他途径。

1)FileList

获取选中的文件信息,还可以用FileList对象,这是在HTML5中新增的,每个表单文件项都有个files属性,里边存储这选中的文件的一些信息

<input type="file" multiple onchange="console.log(this.files);">

选中两个文件后,查看文件信息

JS实现表单多文件上传样式美化支持选中文件后删除相关项

FileList对象看起来是个类数组,有length属性。所以我们应该可以通过修改或删除相关的项来自定义我们选择的文件(注意其实这是不能修改的,且继续看下去)

假如我选择了两个文件,想删除第二项目,使用splice删除,则

<input type="file" multiple onchange="console.log(Array.prototype.splice.call(this.files, 1, 1));">

JS实现表单多文件上传样式美化支持选中文件后删除相关项

报错,由此可知FileList的length属性是只读的,那直接修改为可写可配置呢

Object.defineProperty(FileList.prototype, 'length', {
writable: true,
configurable: true
});

配置之后length能修改了,乍一看还以为splice生效了,然而输出一看,FileList对象内容不变,仍为两项

查阅了一些资料后,了解到浏览器为了安全性的考虑,把FileList对象的内容设为了不可更改,只可以手动置空,但不能修改内容

JS实现表单多文件上传样式美化支持选中文件后删除相关项

JS实现表单多文件上传样式美化支持选中文件后删除相关项

所以,解决办法是,新增一个数组,初始复制FileList对象的文件内容,之后的修改操作则通过这个可更改的数组进行

// 存储更新所选文件
var curFiles = []; 
...
// 选中文件后
var files = this.files;
if (files && files.length) {
// 原始FileList对象不可更改,所以将其赋予curFiles提供接下来的修改
Array.prototype.push.apply(curFiles, files);
}

假如点击了删除叉叉,可以直接更新文件信息数组

var name = $(this).prev().text();
// 去除该文件
curFiles = curFiles.filter(function(file) {
return file.name !== name;
});

这样一来,更新文件信息的问题得到解决,然后就可以进行文件的上传了

点击文件上传,如果直接调用$form.submit(); 则上传的文件信息依然是初始的FileList对象,达不到我们自定义的要求,所以需要用Ajax提交

那么,该怎么想后台提供一个文件对象呢?

2)FormData

HTML5引入了表单的新对象FormData, 它可以生成一个表单对象,我们可以向其中获取/设置键值对信息,再一并提交给后台

引用MDN的FormData使用方法,我们可以添加各种类型的数据,使用ajax提交

var oMyForm = new FormData();
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // 数字123456被立即转换成字符串"123456"
// fileInputElement中已经包含了用户所选择的文件
oMyForm.append("userfile", fileInputElement.files[0]);
var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // Blob对象包含的文件内容
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,不过要注意设置processData和contentType属性为false,防止JQ胡乱解析文件格式

var fd = new FormData(document.getElementById("fileinfo")); // 使用某个表单作为初始项
fd.append("CustomField", "This is some extra data");
$.ajax({
url: "stash.php",
type: "POST",
data: fd,
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false // 告诉jQuery不要去设置Content-Type请求头
});

这里有几个要注意的点:

1)FormData中的属性值接受的是单个文件信息,不能是复合性的对象。可能表意不明,且看

var fd = new FormData($('#form')[0]);
fd.append('myFileTest', curFiles);
$files = $_REQUEST['myFileTest'];
var_dump($files);

用PHP接收传过来的数据,数据却被直接转换成字符串了,非文件对象

JS实现表单多文件上传样式美化支持选中文件后删除相关项

curFiles是文件对象,那PHP端是不是应该用$_FILES来接收信息呢,试试换成$files = $_FILES['myFileTest'];

直接出问题了,说明不能这样处理,需要将curFiles内容一项一项拆开,即单个文件信息

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

JS实现表单多文件上传样式美化支持选中文件后删除相关项

文件接收成功,接下来就可以按需进行文件的操作了

2)后端获取文件信息的时候,是直接通过原始$_FILES获取的,其他一般的信息才用$_REQUEST获取

换成$files = $_REQUEST['myFileTest'];试试,直接就是出现找不到myFileTest的问题

试试添加一般的文件再提交

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

JS实现表单多文件上传样式美化支持选中文件后删除相关项

3)如果需要multiple的多文件上传,则需要在文件项的文件后添加[]号,表示这是一个多文件的数组,以供后端处理解析

fd.append('myFileTest[]', curFiles[i]);

如果没有后面的[],则连续的append会直接覆盖原来的,最后后端获取到的只是最后append进去的项

4)不要直接在JQ的ajax中实例化出一个FormData对象,会出问题

JS实现表单多文件上传样式美化支持选中文件后删除相关项

直接在data属性中生成FormData对象,会被JQ忽略,所以后端什么信息也拿不到

混合表单项简单的例子:

在表单处理中,很多时候我们会进行文件上传和其他基础项的提交,简单地多加一个input项目,看看是否处理成功

<input type="number" name="numberTest" value="100">

JS实现表单多文件上传样式美化支持选中文件后删除相关项

<?php
$files = $_FILES['myFileTest'];
$test = $_REQUEST['numberTest'];
echo json_encode(array(
'len' => count($files['name']),
'num' => $test
));
?>

JS实现表单多文件上传样式美化支持选中文件后删除相关项

以下为全部的JS脚本:

<script type="text/javascript">
/**
* 向文件列表元素中添加相应的文件项
* @param {Array} files 当前的文件列表数组对象
*/
function addItem(files) {
var fileTempItemTpl = $('#file-temp-item-tpl').html(),
fileMoreItemTpl = $('#file-more-item-tpl').html()
htmlTemp = [],
htmlMoreTemp = [],
// 文件列表中各文件坐标位置及所占空间
left = 2,
width = 100,
// 最多取前5个文件
fileTempLen = files.length > 5 ? 5 : files.length;
for (var i = 0, j = files.length; i < j; ++i) {
// 当i > 4,即第6个文件开始
if (i > 4) {
htmlMoreTemp.push(fileMoreItemTpl.replace('{{name}}', files[i].name));
continue;
}
// 计算每一项坐标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)
);
}
// 渲染相关元素内容
$('.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(''));
}
// 保存当前选择的(更新后)文件列表
var curFiles = [];
// 初始选择文件时触发
$('#fileTest').change(function() {
var $this = $(this),
$temp = $('.file-temp'),
files = this.files;
if (files && files.length) {
// 原始FileList对象不可更改,所以将其赋予curFiles提供接下来的修改
Array.prototype.push.apply(curFiles, files);
addItem(curFiles);
$this.hide();
$temp.css('display', 'inline-block');
}
});
$(document)
// 取消选择某个文件时,在文件列表数组对象中删除这个值,并更新列表
.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;
});
// 文件列表数组对象长度大于5才显示“更多文件列表”下拉项
if (curFiles.length <= 5) {
$('.item-more').hide();
}
// 文件列表数组被清空则重置文件选择表单项
if (!curFiles.length) {
$('#fileTest').val('').show();
$('.file-temp').css('display', 'none');
} else {
addItem(curFiles);
}
console.log(curFiles)
})
// 显示“更多文件列表”下拉项
.on('click', '.item-more-btn', function() {
$('.upload-tip').hide();
$('.item-more').show('normal');
});
// 上传操作
$('#uploadBtn').click(function() {
$('.upload-tip').hide();
// 构建FormData对象
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 + '个文件上传成功, number项值为' + rs.num)
.show();
},
error: function(err) {
}
});
});
</script>

以上所述是小编给大家介绍的JS实现表单多文件上传样式美化支持选中文件后删除相关项,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
表单元素事件 (Form Element Events)
Jul 17 Javascript
关于在IE下的一个安全BUG --可用于跟踪用户的系统鼠标位置
Apr 17 Javascript
FF(火狐)浏览器无法执行window.close()解决方案
Nov 13 Javascript
js实现弹窗暗层效果
Jan 16 Javascript
Vue-resource实现ajax请求和跨域请求示例
Feb 23 Javascript
ES6解构赋值的功能与用途实例分析
Oct 31 Javascript
微信小程序实现列表下拉刷新上拉加载
Jul 29 Javascript
微信小程序实现折叠展开效果
Jul 19 Javascript
原生JS实现获取及修改CSS样式的方法
Sep 04 Javascript
详解Vue中数组和对象更改后视图不刷新的问题
Sep 21 Javascript
详解关闭令人抓狂的ESlint 语法检测配置方法
Oct 28 Javascript
vue 通过绑定事件获取当前行的id操作
Jul 27 Javascript
微信小程序 Audio API详解及实例代码
Sep 30 #Javascript
微信小程序 Record API详解及实例代码
Sep 30 #Javascript
微信小程序 Image API实例详解
Sep 30 #Javascript
微信小程序 wx.request(object) API详解及实例代码
Sep 30 #Javascript
JavaScript 链式结构序列化详解
Sep 30 #Javascript
Bootstrap3 Grid system原理及应用详解
Sep 30 #Javascript
CSS3 media queries结合jQuery实现响应式导航
Sep 30 #Javascript
You might like
图象函数中的中文显示
2006/10/09 PHP
使用新浪微博API的OAuth认证发布微博实例
2015/03/27 PHP
PC端微信扫码支付成功之后自动跳转php版代码
2017/07/07 PHP
PHP生成zip压缩包的常用方法示例
2019/08/22 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
2019/12/20 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
理解Javascript_11_constructor实现原理
2010/10/18 Javascript
javascript实现根据身份证号读取相关信息
2014/12/17 Javascript
KVM虚拟化技术之使用Qemu-kvm创建和管理虚拟机的方法
2016/10/05 Javascript
对象不支持indexOf属性或方法的解决方法(必看)
2017/05/28 Javascript
jquery实现一个全局计时器(商城可用)
2017/06/30 jQuery
使用socket.io制做简易WEB聊天室
2018/01/02 Javascript
Node.js Express安装与使用教程
2018/05/11 Javascript
JavaScript选择排序算法原理与实现方法示例
2018/08/06 Javascript
vue实现鼠标移入移出事件代码实例
2019/03/27 Javascript
原生js生成图片验证码
2020/10/11 Javascript
使用Python判断IP地址合法性的方法实例
2014/03/13 Python
python获取从命令行输入数字的方法
2015/04/29 Python
python的文件操作方法汇总
2017/11/10 Python
Python下载网络文本数据到本地内存的四种实现方法示例
2018/02/05 Python
Python实现中一次读取多个值的方法
2018/04/22 Python
Python2包含中文报错的解决方法
2018/07/09 Python
python常见字符串处理函数与用法汇总
2019/10/30 Python
茱莉蔻美国官网:Jurlique美国
2020/11/24 全球购物
公司年会演讲稿范文
2014/01/11 职场文书
会计师职业生涯规划范文
2014/02/18 职场文书
聚美优品广告词改编
2014/03/14 职场文书
环保倡议书范文
2014/05/12 职场文书
县级文明单位申报材料
2014/05/23 职场文书
贫困生助学金感谢信
2015/01/21 职场文书
2015年母亲节活动策划方案
2015/05/04 职场文书
2015年行政人事工作总结
2015/05/21 职场文书
单位领导婚礼致辞
2015/07/28 职场文书
护士工作心得体会
2016/01/25 职场文书
《确定位置》教学反思
2016/02/18 职场文书
Redis集群节点通信过程/原理流程分析
2022/03/18 Redis