您的位置:首页 > 其它

input踩坑大集合,持续更新~

2017-07-13 15:36 423 查看

一、PC端

1、input  type='file'
需求:用户点击按钮弹出选择文件框框,可以多选,可以单选,然后将用户选择的文件缓存,需要在界面显示用户选择了哪些文件, 文件大小不能超过50M,不可以一次上传相同文件两次,上传之前可以逐一删除选择过的文件。
效果如下:



1、文件多选:给input加上 multiple="multiple"即可多选。
2、限制文件后缀:加上accept='.csv'  accept的值就是你想控制的文件后缀名。
3、样式:input file的样式不可改变,将input透明,用div做一个按钮,将input覆盖上面即可。
4、上传文件:项目框架react,UI层用ant design,故第一次开发时候选用了Upload组件,该组件上传一个文件很方便,直接引入组件即可,但是组件有一个缺点,点击就会直接上传,与需求不符。想来想去决定用户选择文件即把文件传给后端,后端将该文件对应的路径返回给我,在用户点击导入的时候,将对应的用户信息和文件路径在给后端传过去。但是由于项目进展,后端共享服务器,用户每次上传的文件会散落在多台服务器上,所以两次上传pass,决定用原生来做。
第一个坑:只用input上传,后台收不到文件
<input onChange={this.changeFiles} multiple="multiple" type="file" accept=".csv" name="name" />


在changeFiles函数中  可以获取到e.target.files。它是一个数组,选择几个文件,数组就有几个元素。好问题解决了,找后台同学调一下。Fuck,后台啥都收不到,怎么传都不行,fetch,ajax,formData都试过了,后台就是收不到东西。
经过一下午的研究终于搞明白了,原来上传文件的input外层,必须要form包裹着,且form要有enctype属性,告诉浏览器我即将要上传的是数据流。
<form encType="multipart/form-data" name="form" id="uploadForm">
<Button type="dashed" width="900">
<div className="DivInButton">
<span style={{fontWeight: "500", color: "#49a9ee", fontSize: "40px"}} className="iconfont icon-upload"></span>
<p style={{fontWeight: "500", fontSize: "16px"}}>选择文件</p>
</div>
</Button>
<input onChange={this.changeFiles} multiple="multiple" type="file" accept=".csv" name="name" />
</form>


如此选择的文件会有FileList标识,只有将这个东西传给服务器,服务器才会收到文件。



第二个坑:如何一个参数对应多个文件。利用formData,循环添加所有文件到一个key上即可。selectFiles就是选择过的文件。
let formData=new FormData();
formData.append("platformType", this.state.platformType);
formData.append("shopName", this.state.shopName);
for (let i = 0;i < selectFiles.length;i++) {
formData.append("files", selectFiles[i]);
}
fetch(env.host+"/order/manualImport", {method: 'POST', body: formData, credentials: "include", header: {'Cache-Control': 'no-cache'}})
.then((response) => {if(/login/.test(response.url)){browserHistory.push('/login');return;};return response.json()})
.then((data) => {
if (data) {
if (data.status === 0) {
do something...
} else {
do something...
}
} else {
do something...
}


5、上传图片预览:利用HTML5的FileReader接口,它的readAsDataURL方法可以获取图片的二进制码,注意,需要将FileReader先加载才可以获取到二进制码。

var EvaluateFile={};
function EvaluateUpload(e) {
EvaluateFile=e.target.files[0];
var reader = new FileReader();
reader.onload = function () {
var _this=this;
document.querySelector(".EvaluateImage").setAttribute("src",_this.result);
document.querySelector(".Evaluate_no").style.display='none';
document.querySelector(".Evaluate_yes").style.display='block';
}
reader.readAsDataURL(EvaluateFile);
}


onload加载完成后,内部this.result就是该图片的二进制码,可以直接输出给img标签。注意上传图片不可以上传二进制码,有些手机拍的照片质量很大,转成二进制码特别特别长,传给后端,后端还需要解析,传输的时间和解析的时间都是性能消耗、浪费时间的。后端的作用就是保存一下嘛。上传图片用方法4即可。

二、移动端

1、input输入框:Android端弹出输入法页面炸裂。

在移动端,大部分情况下页面都是单页式,即最外层容器的宽高都是100%。在ios微信中,用户唤醒输入法是没问题的。
在安卓微信端,页面的可是范围既是document.documentElement.clientHeight,input获取焦点之后,整体高度会变小,导致页面元素位置改变。
这种情况下,要再所有逻辑前边判断当前设备是安卓端还是ios端。如果是安卓端,重新设置一下容器高度为当前设备屏幕高度即可。
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isAndroid) {//安卓
document.getElementById("root").setAttribute("class","Wrap_android");
document.querySelector(".Wrap_android").style.height=document.documentElement.clientHeight+"px";
} else if (isiOS) {//iphone
document.getElementById("root").setAttribute("class","Wrap_iphone");
} else {
document.getElementById("root").setAttribute("class","Wrap_android");
}


2、fixed定位:ios端唤醒输入法之后再滑动页面,定位失效。(不仅限于input输入框,时间选择器、select选择框都有这个bug)
正常的html结构
<body class="layout-fixed">
<!-- fixed定位的头部 -->
<header>

</header>

<!-- 可以滚动的区域 -->
<main>
<!-- 内容在这里... -->
</main>

<!-- fixed定位的底部 -->
<footer>
<input type="text" placeholder="Footer..."/>
<button class="submit">提交</button>
</footer>
</body>


对应的css
header, footer, main {
display: block;
}

header {
position: fixed;
height: 50px;
left: 0;
top: 0;
}

footer {
position: fixed;
height: 34px;
left: 0;
bottom: 0;
}

main {
margin-top: 50px;
margin-bottom: 34px;
height: 2000px
}


这样的结构当用户唤起输入法之后,footer和header的fixed会变成absolute,页面滑动超过一屏时,footer和header就会随之滚动。
这时只要将滚动元素与定位元素分离即可。
<body class="layout-scroll-fixed">
<!-- fixed定位的头部 -->
<header>

</header>

<!-- 可以滚动的区域 -->
<main>
<div class="content">
<!-- 内容在这里... -->
</div>
</main>

<!-- fixed定位的底部 -->
<footer>
<input type="text" placeholder="Footer..."/>

4000
<button class="submit">提交</button>
</footer>
</body>


对应的css
header, footer, main {
display: block;
}

header {
position: fixed;
height: 50px;
left: 0;
top: 0;
}

footer {
position: fixed;
height: 34px;
left: 0;
bottom: 0;
}

main {
/* main绝对定位,进行内部滚动 */
position: absolute;
top: 50px;
bottom: 34px;
/* 使之可以滚动 */
overflow-y: scroll;
/* 增加该属性,可以增加弹性 */
-webkit-overflow-scrolling: touch;
}

main .content {
height: 2000px;
}


注意:移动端使用overflow-y:scroll时,滚动元素内滚动非常不流畅,滑动的手指松开后,滚动立即停止,失去了原本的滚动流畅特性。需使用-webkit-overflow-scrolling:touch;使元素恢复弹性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息