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 相关文章推荐
AeroWindow 基于JQuery的弹出窗口插件
Jun 27 Javascript
基于jQuery和CSS3制作响应式水平时间轴附源码下载
Dec 20 Javascript
DOM中事件处理概览与原理的全面解析
Aug 16 Javascript
jQuery自适应轮播图插件Swiper用法示例
Aug 24 Javascript
Node.js学习入门
Jan 03 Javascript
vue2.0实现倒计时的插件(时间戳 刷新 跳转 都不影响)
Mar 30 Javascript
js实现鼠标跟随运动效果
Aug 02 Javascript
浅谈Vue SPA 首屏加载优化实践
Dec 15 Javascript
vue 中基于html5 drag drap的拖放效果案例分析
Nov 01 Javascript
JavaScript链式调用实例浅析
Dec 19 Javascript
vue自定义指令用法经典实例小结
Mar 16 Javascript
微信小程序实现canvas分享朋友圈海报
Jun 21 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二进制流 逐bit的低位在前算法(详解)
2013/06/13 PHP
PHP获取数组长度或某个值出现次数的方法
2015/02/11 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
2015/03/25 PHP
PHP实现过滤各种HTML标签
2015/05/17 PHP
详谈php静态方法及普通方法的区别
2016/10/04 PHP
php解析base64数据生成图片的方法
2016/12/06 PHP
PHP实现超简单的SSL加密解密、验证及签名的方法示例
2017/08/28 PHP
javascript 支持链式调用的异步调用框架Async.Operation
2009/08/04 Javascript
精选的10款用于构建良好易用性网站的jQuery插件
2011/01/23 Javascript
jquery文字上下滚动的实现方法
2013/03/22 Javascript
jQuery如何跳转到另一个网页 就这么简单
2016/12/28 Javascript
使用JS实现气泡跟随鼠标移动的动画效果
2017/09/16 Javascript
浅谈vue中改elementUI默认样式引发的static与assets的区别
2018/02/03 Javascript
解决Mac安装thrift因bison报错的问题
2018/05/17 Javascript
node.js中 mysql 增删改查操作及async,await处理实例分析
2020/02/11 Javascript
js实现验证码干扰(动态)
2021/02/23 Javascript
python在命令行下使用google翻译(带语音)
2014/01/16 Python
使用url_helper简化Python中Django框架的url配置教程
2015/05/30 Python
Django自定义过滤器定义与用法示例
2018/03/22 Python
利用django-suit模板添加自定义的菜单、页面及设置访问权限
2018/07/13 Python
Python 做曲线拟合和求积分的方法
2018/12/29 Python
Python适配器模式代码实现解析
2019/08/02 Python
Python字符串中添加、插入特定字符的方法
2019/09/10 Python
Python面向对象程序设计之类和对象、实例变量、类变量用法分析
2020/03/23 Python
python 19个值得学习的编程技巧
2020/08/15 Python
CSS3,线性渐变(linear-gradient)的使用总结
2017/01/09 HTML / CSS
解决H5的a标签的download属性下载service上的文件出现跨域问题
2019/07/16 HTML / CSS
会计电算一体化个人简历的自我评价
2013/10/15 职场文书
银行个人求职自荐信范文
2013/12/16 职场文书
函授毕业自我鉴定
2013/12/19 职场文书
我们的节日端午节活动方案
2014/03/02 职场文书
幼儿园中班开学寄语
2014/04/03 职场文书
2015元旦感言
2015/12/09 职场文书
Mysql排序的特性详情
2021/11/01 MySQL
vue动态绑定style样式
2022/04/20 Vue.js
Golang 入门 之url 包
2022/05/04 Golang