移动端input处理
禁用选择复制带来的问题
大多数在禁用选择复制的问题都是通过user-select: none
来处理。代码如下:
* {
-webkit-user-select: none;
/*webkit浏览器*/
-khtml-user-select: none;
/*早期浏览器*/
-moz-user-select: none;
/*火狐*/
-ms-user-select: none;
/*IE10*/
user-select: none;
}
注意,这个是用*禁止所有的元素的选择复制属性,在IOS中会导致inpput、textarea无法获取焦点,应该特别处理
[contenteditable="true"], input, textarea {
-webkit-user-select: auto !important;
-khtml-user-select: auto !important;
-moz-user-select: auto !important;
-ms-user-select: auto !important;
-o-user-select: auto !important;
user-select: auto !important;
}
默认输入框样式处理
/* 去除内部阴影 */
input, textarea {
border: 0; /* 方法1 */
-webkit-appearance: none; /* 方法2 */
}
/* 去掉选中时默认的边框 */
input:focus {
outline: none
}
调用相机,摄像,录音
使用input:file标签, 去调用系统默认相机,摄像,录音功能,capture属性指定功能
<!-- 照相 -->
<input type="file" accept="image/*" capture="camera">
<!-- 摄像 -->
<input type="file" accept="video/*" capture="camcorder">
<!-- 录音 -->
<input type="file" accept="audio/*" capture="microphone">
<!-- 可以添加multiple属性支持多选,但是capture会失效 -->
上传图片案例
- 隐藏input。
```html
<input ref="selectImg" type="file" @change="selectImg"accept="image/*" multiple style="display: none;"/>
```
- 点击按钮触发input的选择事件
```javascript
this.$refs.selectImg.dispatchEvent(new MouseEvent('click'))
```
- 然后绑定change事件,或者file数据流,进行上传、存储等操作。
```javascript
selectImg (e) {
if (e.target.files.length > 0 && e.target.files[0].size === 0) return
if (e.target.files.length > 15) {
this.$vux.toast.text('最多只能选15张图片', 'middle')
return
}
const _self = this
uploadFile(this, e.target.files, (data) => {
let temp = []
data.forEach(item => {
temp.push(item.downloadPath)
})
e.target.value = ''
_self.setSelectPic(temp)
}, null, false)
_self.$router.push('/meiTu/publishDynamic')
}
}
```
数字键盘
一般情况,在input的标签里面type设置为number机可以打开数字键盘。如果有长度限制添加maxlength属性,则会失效
<!-- 一般数字 -->
<input type="number" maxlength="6" pattern="[0-9]*" oninput="if(value.length>6)value=value.slice(0,6)"/>
<!-- 电话号码 -->
<input type="tel" maxlength="11">
软键盘弹起,输入框位置的问题
常见的处理方案:
- 把输入框放在页面顶部,软键盘的高度不会超过页面的高度的一半,这样可避免软键盘弹起对输入框造成的影响。例如顶部搜索框。
- 弹出一个新的页面/窗体来放置输入框。例如:评论、最近搜索
- 聊天类的输入框始终在底部,一般都是通过绝对定位固定在底部的,所以前面两种处理方案都不可行。这样也就带来了兼容性的问题
// 设置输入框始终可见(即软键盘弹出时,如果把输入框遮挡住,输入框会滚到可视区域)
inputElement.scrollIntoView()
inputElement.scrollIntoViewIfNeeded()
可能会出现的问题:
1.1 如果Android的Activity中设置android: windowSoftInputMode = “adjustPan”,窗口不会调用onSizeChanged方法,界面的一部分就会被软键盘覆盖住。
1.2 IOS中整体页面会被网上顶上去,用户体验不友好。
针对上面的问题的处理方案(需要Native提供js接口支持):
第一步, 监听软键盘的打开、关闭状态,并计算出软键盘的高度。(这一步需要Native来做)
第二步, (Android)根据软件的高度变化,来控制输入框的位置。比如:绝对定位的话控制该输入框的bottom属性。非绝对定位则在输入框下面添加等同于键盘高度的div就可以了
第三步, (IOS) IOS中由于软键盘弹起会导致整个页面向上滚动,需要把向上滚动区域的元素重新定位。
IOS中一些莫名其妙的坑
margin-bottom无效
移步另外一篇文章。IOS中margin-bottom无效
transform时z-index无效,以及影响position:fixed
原因: Safari中3D变换会忽略z-index的层级
在Safari浏览器下,此Safari浏览器包括iOS的Safari,iPhone上的微信浏览器,以及Mac OS X系统的Safari浏览器,当我们使用3D transform变换的时候,如果祖先元素没有overflow:hidden/scroll/auto等限制,则会直接忽略自身和其他元素的z-index层叠顺序设置,而直接使用真实世界的3D视角进行渲染。
处理方案
- 父级,任意父级,非body级别,设置overflow:hidden可恢复和其他浏览器一样的渲染。
- 以毒攻毒。页面比较复杂,不方便设置overflow的时候,可设置一个足够大的translateZ值。
IOS点击、或者长按某个区域后有阴影
* {
/* 关键代码 */
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); /* 或者transparent */
}
一些应该注意的地方。
- iPhone X, 在Vue开发中的APP.vue的created中获取window.innerHeight竟然不等于屏幕显示高度,页面重新layout过后竟然又可以了。我没有找到原因。处理方案是 用document.body.offsetHeight代替(前提是body的高度为100%或者100vh)。
- 一般的Android手机、IOS手机的StatusBar的高度为24px,iPhone X中的statusBar的高度为44px,底部的空余的高度为34px。
一些兼容的优化处理
字体大小、宽高比例适配
- 字体:移动端一般默认字体是16px/14px,然后其他字体大小按照比例用rem来做调整。
- 宽高:尽量使用vw, vh单位来做适配。从设计稿中直接拿出宽高的像素值或者做比例缩放,往往只能适配常用的部分手机,在一些手机中的实际效果和设计稿有一些差别。
- 总结,移动设备的多样化,屏幕大小的个性化,做适配也就成了最麻烦的事,特别是iPhone X的刘海设计、18:9的屏占比。因此,用比例值的单位来做适配变得尤为重要。把一些常用的大小、颜色、边距、样式代码块等做成可配置的(Stylus、Sass、Less都支持这样的语法),这样会方便很多。
图片适配
- img, backgroundImage的灵活使用。img用于加载本地图片、图标比较适合,但是加载网络图片失败时就有点尴尬了。backgroundImage比较方便调整图片显示的方式、位置,加载网络图片时,处理为空、加载中时也比较方便灵活。
- 利用七牛云的图片样式,对图片进行处理(裁剪、压缩大小、加水印等)。
页面白屏
一个已经上线的项目,客户反映:IOS9.2、IOS9.3的苹果手机打开白屏。
分析原因
当时猜想是不是IOS9的系统浏览器版本太低,不支持一些语法导致的。
启动IOS9的模拟器,再连接浏览器,查看console的错误日志。果然,有一个错误就是不能识别const,再查看详细内容,是引用的Swiper中的一个swiper.bundle.js导致的。然后再查看Swiper的引用,部分代码还是es6语法。
处理方案
下载离线Swiper,然后在index.html中静态引用。
如果有的组件没有离线版本,可复制源码,然后放在src/目录文件夹下的任何地方,那么打包的时候就会编译成兼容性好的es5语法(前提是引用的babel)
display: grid语法不支持
这个可以用其他的一些写法实现。
- 使用table
- 使用float
比较麻烦的是怎么实现grid布局中的gap?box-sizing: border-box
可以实现这一功能。
box-sizing 属性值说明:
content-box
默认值,标准盒子模型。 width 与 height 只包括内容的宽和高, 不包括边框(border),内边距(padding),外边距(margin)。注意: 内边距, 边框 & 外边距 都在这个盒子的外部。 比如. 如果 .box {width: 350px}; 而且 {border: 10px solid black;} 那么在浏览器中的渲染的实际宽度将是370px;
尺寸计算公式:width = 内容的宽度,height = 内容的高度。宽度和高度都不包含内容的边框(border)和内边距(padding)。
border-box
width 和 height 属性包括内容,内边距和边框,但不包括外边距。这是当文档处于 Quirks模式 时Internet Explorer使用的盒模型。注意,填充和边框将在盒子内 , 例如, .box {width: 350px; border: 10px solid black;} 导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。
这里的维度计算为:
width = border + padding + 内容的 width,
height = border + padding + 内容的 height。
padding-box
width 和 height 属性包括内容和内边距,但是不包括边框和外边距。只有Firefox实现了这个值,它在Firefox 50中被删除。
实现
参考element-ui中的Row、Col组件。