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 相关文章推荐
超级兔子让浮动层消失的前因后果
Mar 09 Javascript
javascript使用正则表达式实现去掉空格之后的字符
Feb 15 Javascript
jquery.map()方法的使用详解
Jul 09 Javascript
jQuery Easyui快速入门教程
Aug 21 Javascript
Bootstrap响应式表格详解
May 23 Javascript
浅谈gulp创建完整的项目流程
Dec 20 Javascript
详解webpack4多入口、多页面项目构建案例
May 25 Javascript
vuejs使用axios异步访问时用get和post的实例讲解
Aug 09 Javascript
微信小程序实现banner图轮播效果
Jun 28 Javascript
react的滑动图片验证码组件的示例代码
Feb 27 Javascript
使用element-ui的el-menu导航选中后刷新页面保持当前选中状态
Jul 19 Javascript
JavaScript Html实现移动端红包雨功能页面
Jan 10 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
php页面缓存ob系列函数介绍
2012/10/18 PHP
div li的多行多列 无刷新分页示例代码
2013/10/16 PHP
php分页代码学习示例分享
2014/02/20 PHP
ThinkPHP3.1新特性之命名范围的使用
2014/06/19 PHP
ThinkPHP结合ajax、Mysql实现的客户端通信功能代码示例
2014/06/23 PHP
ThinkPHP模版中导入CSS和JS文件的方法
2014/11/29 PHP
php计算两个坐标(经度,纬度)之间距离的方法
2015/04/17 PHP
让firefox支持IE的一些方法的javascript扩展函数代码
2010/01/02 Javascript
js获取元素在浏览器中的绝对位置
2010/07/24 Javascript
juqery 学习之四 筛选过滤
2010/11/30 Javascript
js取消单选按钮选中并判断对象是否为空
2013/11/14 Javascript
AngularJS基础 ng-mousemove 指令简单示例
2016/08/02 Javascript
AngularJS实现表单验证功能详解
2017/10/12 Javascript
webpack打包并将文件加载到指定的位置方法
2018/02/22 Javascript
vue修改对象的属性值后页面不重新渲染的实例
2018/08/09 Javascript
js实现小星星游戏
2020/03/23 Javascript
Angular8 简单表单验证的实现示例
2020/06/03 Javascript
基于vue中的scoped坑点解说
2020/09/04 Javascript
深入解析Python中的集合类型操作符
2015/08/19 Python
Linux 发邮件磁盘空间监控(python)
2016/04/23 Python
Python读取一个目录下所有目录和文件的方法
2016/07/15 Python
python中logging包的使用总结
2018/02/28 Python
Python3 replace()函数使用方法
2018/03/19 Python
解决python中无法自动补全代码的问题
2018/12/04 Python
Python获取好友地区分布及好友性别分布情况代码详解
2019/07/10 Python
CSS3用@font-face实现自定义英文字体
2013/09/23 HTML / CSS
html5中监听canvas内部元素点击事件的三种方法
2019/04/28 HTML / CSS
宏碁西班牙官网:Acer西班牙
2021/01/08 全球购物
将"引用"作为函数返回值类型的格式、好处和需要遵守的规则
2016/02/09 面试题
计算机本科生自荐信
2013/10/15 职场文书
会计辞职信范文
2014/01/15 职场文书
小学生期末评语大全
2014/04/21 职场文书
《陈毅探母》教学反思
2014/05/01 职场文书
2014大学校园光棍节活动策划书
2014/09/29 职场文书
mysql备份策略的实现(全量备份+增量备份)
2021/07/07 MySQL
Java存储没有重复元素的数组
2022/04/29 Java/Android