移动端兼容性问题总结

1. ios 移动端页面对点击事件有 300ms 延时

使用 fastclick 库 https://github.com/ftlabs/fastclick

使用 FastClick 的时候,在需要使用的层上,实例化它。我们使用 document.body 是因为希望所有的按钮和链接都获得快速点击

import FastClick from 'fastclick'
FastClick.attach(document.body)

2. ios 滚动卡顿

div {
-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
/* -webkit-overflow-scrolling: auto; */ /* 当手指从触摸屏上移开,滚动会立即停止 */
}

3. ios 1px border 变宽

以 dpr = 2 为例:
你拿到一张标准的基于 iphone6 的设计稿(750px)
你看到它设计的一个 border 宽度是 1px
你兴致勃勃地写下了 border: 1px solid #000;
然而 iphone6 实际渲染像素是 375px,那么设计需要 border 的其实是 border: 0.5px solid #000;
然后你的是 1px
不是 1px 变粗了,只是实际只是需要 0.5px 而已

<meta name="viewport" content="width=device-width"> 意思是将物理设备的宽度设置给当前浏览器

在使用 table 标签设置 border: 1px 并使用 border-collapse: collapse; 合并边框后,发现 td 之间的边框宽度并不是 1px,而是比 1px 宽,大概为 1.5px

4. webapp 软键盘弹起时问题

其他参考链接 https://segmentfault.com/a/1190000010693229
https://github.com/ioing/IOING

页面放大:

<meta name="viewport" content="user-scalable=no" />

输入框被遮挡,看不见输入的内容: document.activeElement.scrollIntoView()

页面自动上移,但收回软键盘时页面没有恢复原样少了一截

fixed 定位效果失效: ios 弹出软键盘的时候, webview 的高度没有变化导致超出屏幕范围,并且不会触发 resize 事件

scrollIntoView 与 scrollIntoViewIfNeeded

Element.scrollIntoView(option) 方法让当前的元素滚动到浏览器窗口的可视区域内
option 如果为 true,元素的顶端将和其所在滚动区的可视区域的顶端对齐, 默认
option 如果为 false,元素的底端将和其所在滚动区的可视区域的底端对齐

element.scrollIntoViewIfNeeded() 用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。 如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动

var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
var innerHeight = window.innerHeight
// 键盘弹起时 scrollHeight innerHeight 发生变化
<input @focus="input(1)" @blur="input(2)" />
input (num) {
var u = navigator.userAgent
if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {
// 安卓手机通过 resize 事件监听键盘事件,因为部分手机手动关闭键盘时并不会失焦
} else if (u.indexOf('iPhone') > -1) {
// 苹果手机
if (num === 1) {
// 键盘弹起
document.activeElement.scrollIntoView()
} else {
// 键盘隐藏
var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0
// window.scrollTo(0, Math.max(scrollHeight - 1, 0))
window.scrollTo(0, 0)
}
}
}

附:安卓手机监听 resize

data () {
return {
originalHeight: document.documentElement.clientHeight || document.body.clientHeight,
resizeHeight: document.documentElement.clientHeight || document.body.clientHeight
}
},
watch: {
resizeHeight (val) {
var u = navigator.userAgent
if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {
// var detail = document.querySelector('.detail')
if (this.resizeHeight - 0 < this.originalHeight - 0) {
// detail.style.paddingBottom = '260px'
document.activeElement.scrollIntoView({ behavior: 'auto', block: 'start' })
} else {
// detail.style.paddingBottom = '0'
}
}
}
},
mounted () {
const that = this
window.onresize = () => {
return (() => {
window.resizeHeight = document.documentElement.clientHeight || document.body.clientHeight
that.resizeHeight = window.resizeHeight
})()
}
}

5. 手机端页面文件上传兼容性问题

6. 移动端和 PC 端中的 hover 处理 移动端点击时会有 pc 端 hover 效果

百度一下给出的方式是注册 touchstart 事件

document.body.addEventListener('touchstart', function() {})

但经过测试并没有解决该问题

7. 移动端 touch 时,会触发 pc 端的 mouseenter mouseleave 事件

在事件中通过判断屏幕宽度解决

8. ios a 链接 input type=”file” 等在点击时会出现灰色(touch 高亮)

-webkit-tap-highlight-color: transparent;

9. 禁用浏览器自动调整字体大小

移动端浏览器切换橫向模式时会调整字体大小(字体变大),解决方式:

html {
-webkit-text-size-adjust: none; /* 或 100% */
}

谷歌浏览器已不支持这个属性了,不能通过该方式实现小于 12px 的字体,可使用缩放(transform:scale(0.8))来实现小于 12px 的字体

10. appearance 属性

normal|icon|window|button|menu|field
所有主流浏览器都不支持 appearance 属性

-webkit-appearance: none; 去除默认样式,使 ios 端和安卓端显示效果一样,但有一个问题,input 的 checkbox 和 radio 类型在安卓端可能无法正常显示

11. 禁止长按

在 iOS 上,当你触摸并按住触摸的目标,比如长按一个链接,浏览器将显示链接有关的系统默认菜单,
长按图像弹出选项存储或者拷贝图像,长按文字弹出选择文字菜单

可通过如下方式禁止这些行为

禁止长按图片保存

img {
-webkit-touch-callout: none;
pointer-events: none; // 像微信浏览器还是无法禁止,加上这行样式即可
}

禁用长按复制

user-select: none;

禁止长按呼出菜单

div {
-webkit-touch-callout: none;
}

12. 点击穿透

假如页面上有两个元素 A 和 B。B 元素在 A 元素之上。我们在 B 元素的 touchstart 事件上注册了一个回调函数,该回调函数的作用是隐藏 B 元素。我们发现,当我们点击 B 元素,B 元素被隐藏了,随后,A 元素触发了 click 事件。
这是因为在移动端浏览器,事件执行的顺序是 touchstart > touchend > click。而 click 事件有 300ms 的延迟,当 touchstart 事件把 B 元素隐藏之后,隔了 300ms,浏览器触发了 click 事件,但是此时 B 元素不见了,所以该事件被派发到了 A 元素身上。如果 A 元素是一个链接,那此时页面就会意外地跳转。
跨页面点击穿透问题 点击页内按钮跳转至新页,然后发现新页面中对应位置元素的 click 事件被触发了

13. 移动端 video 播放时不弹出页面层

<video x5-playsinline="" playsinline="" webkit-playsinline=""></video>

14. vue 移动端监听 scroll

mounted () {
document.querySelector('.list').addEventListener('scroll', () => {
this.scroll = document.querySelector('.list').scrollTop
})
}

15. vue 移动端列表保存滚动位置

beforeRouteEnter (to, from, next) {
next((vm) => {
// console.log(vm.$route.meta.scrollTop)
if (vm.$route.meta.scrollTop) {
document.querySelector('.list').scrollTop = vm.$route.meta.scrollTop
}
})
},

beforeRouteLeave (to, from, next) {
if (to.name === 'invite') {
this.$store.commit('removeIncludeComponent', 'staff')
// next()
}
if (to.name === 'staffDetail') {
let top = document.querySelector('.list').scrollTop
console.log(top)
this.$route.meta.scrollTop = top
}
next()
}

16. 移动端去除 type 为 number 的箭头

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
margin: 0;
}

17. 设置状态栏的背景颜色(IOS)

设置状态栏的背景颜色,只有在”apple-mobile-web-app-capable” content=”yes”时生效

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

content 参数:

default :状态栏背景是白色。
black :状态栏背景是黑色。
black-translucent :状态栏背景是半透明。 如果设置为 default 或 black ,网页内容从状态栏底部开始。
如果设置为 black-translucent ,网页内容充满整个屏幕,顶部会被状态栏遮挡。

18. 弹出数字键盘

<!-- 有"#" "*"符号输入 -->
<input type="tel" />

<!-- 纯数字 -->
<input pattern="\d*" />

打开原生应用

某些浏览器会禁用此协议,比如微信内部浏览器(除非开了白名单)

<a href="weixin://">打开微信</a>
<a href="alipays://">打开支付宝</a>
<a href="alipays://platformapi/startapp?saId=10000007">打开支付宝的扫一扫功能</a>
<a href="alipays://platformapi/startapp?appId=60000002">打开支付宝的蚂蚁森林</a>

解决 IOS下 active 伪类失效

给 body 注册一个空事件即可

<body ontouchstart></body>

最简单的 rem 实现

@media (min-width: 640px) {
html {
font-size: calc(640px / 3.75);
}
}
@media (min-width: 320px) and (max-width: 640px) {
html {
font-size: calc(100vw / 3.75);
}
}

better-scroll 解决移动端滚动问题

https://github.com/ustbhuangyi/better-scroll/

iphone X 适配 - 安全区域(safe area)

参考:https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/

在 iOS 11 中采用了 viewport-fit 的 meta 标签作为适配方案

viewport-fit 取值

auto 默认:页面内容显示在 safe area 内
cover 页面内容充满屏幕,可以通过添加边距保证网页主要内容处于 safe area 中不被裁剪

iOS 11 的 webview 引入了 constantenv 来处理 viewport-fit=cover 属性,以及一组四个预定义的常量:safe-area-inset-left, safe-area-inset-right, safe-area-inset-topsafe-area-inset-bottom,分别表示 safe area 和可视窗口 viewport 顶部,右边,左边,底部的间距,可以用于设置 margin 和 padding 或者绝对定位时 left 和 top,这四个常量只有在 viewport-fit=cover 时有效

建议使用 viewport-fit=cover 因为会使用 auto 会造成屏幕四周出现白边

<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
/*  top 为 0 的 fixed 定位的 heade */
header {
position: fixed;
top: 0;
/* ... */
/* Status bar height on iOS 10 */
padding-top: 20px;
/* Status bar height on iOS 11.0 */
padding-top: constant(safe-area-inset-top);
/* Status bar height on iOS 11+ */
padding-top: env(safe-area-inset-top);
}
body {
padding-top: constant(safe-area-inset-top); // 为导航栏+状态栏的高度 88px
padding-left: constant(safe-area-inset-left); // 如果未竖屏时为0
padding-right: constant(safe-area-inset-right); // 如果未竖屏时为0
padding-bottom: constant(safe-area-inset-bottom); // 为底下圆弧的高度 34px
}