简介
这篇文章主要是总结下移动端开发常见问题,帮助大家一起避坑。如果已经看过这篇文章了的话也可以看看笔者写的
HTML方面
调用系统功能
<!-- 拨打电话 -->
<a href="tel:10010">拨打电话给10010</a>
<!-- 发送短信 -->
<a href="sms:10010">发送短信给10010</a>
<!-- 发送邮件 -->
<a href="mailto:randy@qq.com">发送邮件给randy</a>
<!-- 选择照片或拍摄照片 -->
<input type="file" accept="image/*" />
<!-- 选择视频或拍摄视频 -->
<input type="file" accept="video/*" />
<!-- 选择文件 -->
<input type="file" />
<!-- 多选文件 -->
<input type="file" multiple />
忽略自动识别
有时电话、邮箱的自动识别会很烦,我们可以关闭。
<!-- 忽略自动识别电话 -->
<meta name="format-detection" content="telephone=no" />
<!-- 忽略自动识别邮箱 -->
<meta name="format-detection" content="email=no" />
<!-- 忽略自动识别电话和邮箱 -->
<meta name="format-detection" content="telephone=no, email=no" />
弹出数字键盘
这里需要注意,系统需要使用的是原生键盘才会生效,不能使用搜狗等输入法,不然只会出现相应输入法的数字键盘。
<!-- 纯数字带#和* -->
<input type="tel">
<!-- 纯数字 -->
<input type="number" pattern="\d*">
优先使用最新版本 IE 和 Chrome
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
禁止页面缓存
<meta http-equiv="Cache-Control" content="no-cache" />
关闭首字母大写
当我们使用键盘在输入框输入英文字符的时候,一般首字母会自动大写,如果不需要可以关闭。
<input type="text" autocapitalize="off" />
关闭输入自动修正
和英文输入默认自动首字母大写那样,IOS还做了一个功能,默认输入法会开启自动修正输入内容,这样的话,用户经常要操作两次。如果不希望开启此功能,我们可以通过input标签属性来关闭掉。
<input type="text" autocorrect="off" />
禁止页面缩放
不想让页面缩放我们可以把viewport的user-scalable值设置为no,这个在上篇文章介绍viewport的时候有说到。
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1" />
长按识别二维码
有时候我们会接到需求,就是长按二维码要自动识别出二维码,其实这个是最简单的,就是把我们的图片使用img
标签展示出来就可以了。记住不要使用背景图,不然会无效。
CSS方面
1px问题
1px问题在Retina 高清屏上才会出现,由于高清屏用多个物理像素显示一个css像素,比如iphone6,由于dpr为2,所以1css像素会用2个物理像素显示,所以看起来1px的线条会特别宽。
解决1px问题的方案有很多,有背景图、阴影、缩放等等,笔者在这里只介绍笔者曾经使用过得方案,就是缩放。原理就是边框固定,把元素整体先放大然后再缩小。
.ele {
position: relative;
width: 100px;
height: 80px;
&::after {
content: "";
position: absolute;
left: 0;
top: 0;
border: 1px solid red;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
}
}
如果不想给每个元素都这样设置的话,我们可以采用笔者前面写的移动端H5网页开发必备知识里面介绍的lib-flexible
方案,根据dpr将网页整体进行缩放。
横屏和竖屏
在某些特定横屏或者竖屏的情况下的样式我们可以通过如下@media查询进行设置。
/* 竖屏 */
@media all and (orientation: portrait) {
/* 自定义样式 */
}
/* 横屏 */
@media all and (orientation: landscape) {
/* 自定义样式 */
}
让苹果手机滚动更流畅
在苹果手机上滚动的时候有时候可能会不流畅,我们可以给元素添加如下属性。
.elem {
-webkit-overflow-scrolling: touch;
}
让苹果手机页面里的字体变清晰
.elem {
-webkit-font-smoothing:antialiased;
}
-webkit-font-smoothing
在window
系统下没有起作用,但是在IOS
设备上是起作用的,灰度平滑。
禁止选择
有时候我们的图片不想让别人长按保存等操作,我们可以使用如下样式,让用户长按图片无效。
#img1 {
pointer-events: none; /* 微信浏览器还需附加该属性才有效 */
user-select: none;
-webkit-touch-callout: none;
}
原生表单美化
我们使用原生表单的时候样式会很丑,特别还有一层灰色的遮罩,我们可以使用如下样式把遮罩进行消除。小伙伴们是不是感觉清爽了很多呢。
button,
input,
select,
textarea {
-webkit-appearance: none;
}
元素被点击时产生的半透明灰色遮罩怎么去掉
当使用原生表单的时候,点击的时候会有个灰色点击特效,如果不需要可以使用如下样式去掉。
a,button,input,textarea,select {
-webkit-tap-highlight-color: transparent;
}
美化滚动条
滚动条样式太丑希望自定义,我们可以使用如下样式属性进行自定义样式。这个是pc端和移动端通用的样式。
- ::-webkit-scrollbar:滚动条整体部分
- ::-webkit-scrollbar-track:滚动条轨道部分
- ::-webkit-scrollbar-thumb:滚动条滑块部分
::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
border-radius: 3px;
background-image: linear-gradient(90deg, #09f, #3c9);
}
隐藏滚动条
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
美化占位符
这个是pc端和移动端通用的样式,当我们需要修改占位符placeholder的颜色我们可以使用如下样式。
input::placeholder {
color: red;
}
输入框文字居中对齐
有时候我们就算设置了line-height的值等于height,文字看起来也不居中,我们可以使用如下样式。
input {
line-height: normal;
}
修改input光标颜色
这种方法不会影响字体颜色,只会修改光标颜色。
input {
caret-color: red;
}
当然你也可以可以设置字体颜色来修改光标颜色。但是这种方式会将字体颜色也一起修改。
input {
color: red;
}
删除 type="number"
末尾的箭头
.no-arrow::-webkit-outer-spin-button,
.no-arrow::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input自动填充时出现背景颜色
比如,如果保存了账号密码,自动填充会有一个背景颜色。
解决就是给它设置一个足够大的白色内阴影。
:-webkit-autofill {
box-shadow: 0 0 0px 1000px white inset !important;
}
这样设置后背景色会没有,但是自动填充的内容文本默认是黑色的。
如果也想让字体颜色自定义的话还需要加上如下样式。比如笔者这里设置的蓝色。
:-webkit-autofill {
-webkit-text-fill-color: #4c7fd2 !important;
}
这样就完美了。
怎么解决img底部空白间隙问题
- 将
img
元素设置为display: block
- 将
img
元素设置为vertical-align: bottom
- 设置父元素
font-size
设为0
全网页置灰
html {
filter: grayscale(100%);
}
如果需要具体到某元素置灰,把该样式应用到某具体元素即可。
文本溢出省略号
当我们需要将多于文本用省略号显示的时候,我们可以用到如下样式。其实这个是pc端和移动端通用的样式。
使用这个样式需要注意,元素必须是块级元素,或者是具有指定宽度的行内块级元素(就是display: inline-block
)。
单行省略
/* 单行出现省略号*/
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
多行省略
/* 多行省略号,就是想在多少行后再出现省略号就使用如下样式*/
.ellipsis-n {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2; /*这个值可以随便设置,比如这里的2就是最多显示两行,两行后出现省略号*/
-webkit-box-orient: vertical;
}
注意,包裹文字的元素不能有固定高度或者下外边距,否则可能会有文字漏出。
图片模糊问题
这个类似于1px问题,在Retina 高清屏上才会出现,由于高清屏用多个物理像素显示一个css像素,比如iphone6(750个物理像素375个css像素),由于dpr为2,所以1css像素会用2个物理像素显示,所以1像素的图片用2个物理像素显示出来就会模糊。
解决图片模糊根本原因就是根据不同dpr使用不同倍数的图片。
@media查询
.avatar{
background-image: url(randy_1x.png);
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
.avatar{
background-image: url(randy_2x.png);
}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
.avatar{
background-image: url(randy_3x.png);
}
}
image-set
.avatar {
background-image: -webkit-image-set( "randy_1x.png" 1x, "randy_2x.png" 2x );
}
srcset
这里需要注意,这里的图片不能被webpack处理,就是不能被转成base64,不然会失效。
<img src="randy_1x.png" srcset="randy_2x.png 2x, randy_3x.png 3x" />
js处理
const dpr = window.devicePixelRatio;
const images = document.querySelectorAll('img');
images.forEach((img)=>{
img.src.replace(".", `${dpr}x.`);
})
svg
当然,我们还可以使用不失帧的svg图片,这样就不需要管是什么屏幕了,svg图片会自动拉伸并且不会失帧。
<img src="randy.svg" />
JS方面
点击延迟
在移动端浏览器里点击操作会存在300ms
延迟。
07年,苹果公司发布首款Iphone前夕,遇到一个问题:当时的网站都是为大屏设计,手机屏幕太小无法正常浏览,于是苹果工程师做了一些约定解决此类问题。
这些约定当中,最为有名的是双击缩放(double tap to zoom),这是产生300ms延迟的根源。
用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。如果用户在 iOS Safari
里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。 由此产生了300ms延迟问题。
解决点击延迟的方案有很多,下面笔者着重介绍四种。
禁止页面缩放
我们知道点击延迟的根源就是因为页面缩放,所以我们可以把页面禁止缩放,就能从源头上解决该问题了,但是该方案的缺点也很明显,就是页面不能缩放了。
<!-- 1.禁用缩放 user-scalable=no -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
更改默认的视口宽度
<meta name="viewport" content="width=device-width" />
因为双击缩放主要是用来改善桌面站点在移动端浏览体验的,而随着响应式设计的普及,很多站点都已经对移动端坐过适配和优化了,这个时候就不需要双击缩放了,如果能够识别出一个网站是响应式的网站,那么移动端浏览器就可以自动禁掉默认的双击缩放行为并且去掉300ms的点击延迟。如果设置了上述meta
标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。
这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。
使用zepto
在前端领域里最早解决点击延迟的是jQuery时代
的zepto
,估计现在大部分同学都未使用过zepto
,其实它就是移动端版本的jquery
。zepto
封装tap事件
能有效地解决点击穿透,我们知道移动端点击事件触发的顺序为 touchstart —> touchmove —> touchend —> click
,通过监听document
上的touch事件
(因为click事件在移动端有300ms延迟但是touch事件在移动端没延迟)完成tap事件
的模拟,并将tap事件
冒泡到document
上触发模拟移动端的click事件。
虽然zepto能解决点击延迟问题,但是也会带来点击穿透问题,就是如果某元素下面真实有绑定click的元素,我们知道事件具有传播性,就会在tap事件后会继续触发click,这样就会带来点击穿透问题。
说道这里,细心的小伙伴会发现了,为什么不在touchend
事件里面使用e.preventDefault()
来阻止后续事件触发呢?其实这跟zepto的tap事件实现有关,因为zepto的tap事件是全局监听touchend
事件的,所以如果使用了e.preventDefault()
会影响到真正的touchend
事件。
使用fastclick
fastclick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。fastclick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
fastclick不但能解决点击延迟而且不会带来点击穿透问题,是点击延迟的最优解。具体使用方法可以查看官方文档。
时间问题
在苹果系统上解析YYYY-MM-DD HH:mm:ss
这种日期格式会报错,但在安卓系统上解析这种日期格式完全无问题。但是YYY/MM/DD HH:mm:ss
这种日期格式在苹果和安卓上都能正常运行,所以我们只需要统一使用这种方案就可以了。
const date = "2010-12-31 12:30:00";
new Date(date.replace(/\-/g, "/"));
滚动到指定位置
这个技巧在pc端和移动端都适用,使用Element.scrollIntoView()方法,我们再也不需要使用scrollBy()
和scrollTo()
来实现滚动到指定位置了。
element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型参数
element.scrollIntoView(scrollIntoViewOptions); // Object型参数
alignToTop
可选,一个Boolean
值:
- 如果为
true
,元素的顶端将和其所在滚动区的可视区域的顶端对齐。相应的scrollIntoViewOptions: {block: "start", inline: "nearest"}
。这是这个参数的默认值。 - 如果为
false
,元素的底端将和其所在滚动区的可视区域的底端对齐。相应的scrollIntoViewOptions: {block: "end", inline: "nearest"}
。
scrollIntoViewOptions
可选,一个包含下列属性的对象:
behavior
可选,定义动画过渡效果,"auto"
或"smooth"
之一。默认为"auto"
。block
可选,定义垂直方向的对齐,"start"
,"center"
,"end"
, 或"nearest 就近对齐"
之一。默认为"start"
。inline
可选,定义水平方向的对齐,"start"
,"center"
,"end"
, 或"nearest 就近对齐"
之一。默认为"nearest 就近对齐"
。
横屏和竖屏
window.addEventListener("resize", ()=>{
if (window.orientation === 180 || window.orientation === 0) {
// 正常方向或屏幕旋转180度
console.log('竖屏');
};
if (window.orientation === 90 || window.orientation === -90 ){
// 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
console.log('横屏');
}
});
禁止网页复制
const html = document.querySelector('html');
html.oncopy = () => false;
iPhoneX适配
viewport-fit
viewport-fit
是专门为了适配iPhoneX
而诞生的一个属性,它用于限制网页如何在安全区域内进行展示。
contain
: 可视窗口完全包含网页内容cover
:网页内容完全覆盖可视窗口
默认情况下或者设置为auto
和contain
效果相同。
<meta name="viewport" content="viewport-fit=cover" />
env、constant
我们需要将顶部和底部合理的摆放在安全区域内,iOS11
新增了两个CSS
函数env、constant
,用于设定安全区域与边界的距离。
constant
在iOS < 11.2
的版本中生效,env
在iOS >= 11.2
的版本中生效,这意味着我们往往要同时设置他们,将页面限制在安全区域内:
函数内部可以是四个常量:
safe-area-inset-left
:安全区域距离左边边界距离safe-area-inset-right
:安全区域距离右边边界距离safe-area-inset-top
:安全区域距离顶部边界距离safe-area-inset-bottom
:安全区域距离底部边界距离
注意:我们必须指定viweport-fit
后才能使用这两个函数。
body {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
深色主题适配方案
随着 iOS 13 的发布,深色模式(Dark Mode)越来越多地出现在大众的视野中,支持深色模式已经成为现代移动应用和网站的一个潮流,前段时间更是因为微信的适配再度引起热议。深色模式不仅可以大幅减少电量的消耗,减弱强光对比,还能提供更好的可视性和沉浸感。
如何切换深色模式
- iOS:设置->显示与亮度->外观->选择深色。
- Android:系统设置->显示->深色模式。
适配方案
如果系统设置了深色模式,H5页面不做相应的处理,会出现背景色冲突、深色文字显示异常,深色图标显示异常等一些显示上的问题。
声明 color-scheme
在head中声明<meta name="color-scheme" content="light dark">
,声明当前页面支持 light 和 dark 两种模式,系统切换到深色模式时,浏览器默认样式也会切换到深色;具体细节可以查看 color-scheme 文档。
或者使用css申明,也能达到同样的效果。
:root {
color-scheme: light dark;
}
prefers-color-scheme
prefers-color-scheme CSS 媒体特性用于检测用户是否有将系统的主题色设置为亮色或者暗色。我们可以通过监听用户所设置的主题来进行一些特殊样式的处理。
- no-preference
表示系统未得知用户在这方面的选项。在布尔值上下文中,其执行结果为 false。
- light
表示用户已告知系统他们选择使用浅色主题的界面。
- dark
表示用户已告知系统他们选择使用暗色主题的界面。
:root {
color-scheme: light dark;
background: white;
color: black;
}
@media (prefers-color-scheme: dark) {
:root {
background: black;
color: white;
}
}
JS中的处理
除了CSS的媒体查询能获取用户主题,在JS中我们也能获取得到用户的主题。
matchMedia
Window 的matchMedia() 方法返回一个新的MediaQueryList 对象,表示指定的媒体查询 (en-US)字符串解析后的结果。返回的MediaQueryList 可被用于判定Document是否匹配媒体查询,或者监控一个document 来判定它匹配了或者停止匹配了此媒体查询。
const mqList = window.matchMedia(mediaQueryString)
addListener()
MediaQueryList接口的addListener()方法向MediaQueryListener添加一个侦听器,该侦听器将运行自定义回调函数以响应媒体查询状态的更改。
MediaQueryList.addListener(func)
在JS中需要这两者配合使用
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
function darkModeHandler() {
if (mediaQuery.matches) {
console.log('现在是深色模式')
} else {
console.log('现在是浅色模式')
}
}
// 监听模式变化
mediaQuery.addListener(darkModeHandler)
禁止微信浏览器放大字体
微信内置了调整字体大小的功能,用户可以根据实际情况进行调节。但是这也会导致字体大小改变以后,出现页面布局错乱的情况。
IOS解决方案
IOS
的解决方案是覆盖掉微信的样式
body {
/* IOS禁止微信调整字体大小 */
-webkit-text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
}
安卓解决方案
安卓比较麻烦,需要用js
来处理。安卓的解决方案是通过 WeixinJSBridge
对象将网页的字体大小设置为默认大小,并且重写设置字体大小的方法,让用户不能在该网页下设置字体大小
(function () {
if (
typeof WeixinJSBridge == "object" &&
typeof WeixinJSBridge.invoke == "function"
) {
handleFontSize();
} else {
if (document.addEventListener) {
document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);
} else if (document.attachEvent) {
document.attachEvent("WeixinJSBridgeReady", handleFontSize);
document.attachEvent("onWeixinJSBridgeReady", handleFontSize);
}
}
function handleFontSize() {
// 设置网页字体为默认大小
WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 });
// 重写设置网页字体大小的事件
WeixinJSBridge.on("menu:setfont", function () {
WeixinJSBridge.invoke("setFontSizeCallback", { fontSize: 0 });
});
}
})();
扩展
判断是否在微信浏览器
export const isWechatBrowser = () => {
const ua = navigator.userAgent.toLowerCase();
return ua.indexOf('micromessenger') != -1;
}
检查是否在苹果设备上
const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
console.log(isAppleDevice);
检查设备是移动设备还是电脑
const detectDeviceType = () =>
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent)
? 'Mobile'
: 'Desktop';
console.log(detectDeviceType()); // "Mobile" or "Desktop"
系列文章
参考文章
中高级前端必须注意的40条移动端H5坑位指南 | 网易三年实践
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!