H5 中遇到的问题

... 2020-10-15 About 13 min

# H5 中遇到的问题

# 1. 数字键盘

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

<!-- 纯数字 -->
<input pattern="\d*" />
1
2
3
4
5

# 2. 调用系统功能

<!-- 拨号 -->
<a href="tel:10086">打电话给: 10086</a>

<!-- 发送短信 -->
<a href="sms:10086">发短信给: 10086</a>

<!-- 发送邮件 -->
<a href="mailto:839626987@qq.com">发邮件给:839626987@qq.com</a>

<!-- 选择照片或者拍摄照片 -->
<input type="file" accept="image/*" />

<!-- 选择视频或者拍摄视频 -->
<input type="file" accept="video/*" />

<!-- 多选 -->
<input type="file" multiple />

<!-- 在先知道应用链接的情况下面 -->
<a href="weixin://">打开微信</a>
<a href="alipays://">打开支付宝</a>
<a href="alipays://platformapi/startapp?saId=10000007"
  >打开支付宝的扫一扫功能</a
>
<a href="alipays://platformapi/startapp?appId=60000002">打开支付宝的蚂蚁森林</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 3. 伪类 active 失效的问题

<body ontouchstart></body>
1

# 4. 忽略自动识别

<!-- 忽略浏览器自动识别数字为电话号码 -->
<meta name="format-detection" content="telephone=no" />

<!-- 忽略浏览器自动识别邮箱账号 -->
<meta name="format-detection" content="email=no" />
1
2
3
4
5

# 5. 解决 input 失焦后页面没有回弹

<template>
  <input type="text" @focus="focus" @blur="blur" />
</template>

<script>
export default {
  data() {
    return {
      scrollTop: 0,
    };
  },
  methods: {
    focus() {
      this.scrollTop = document.scrollingElement.scrollTop;
    },
    blur() {
      document.scrollingElement.scrollTo(0, this.scrollTop);
    },
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 6. 禁止长按

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

// 禁止长按选择文字
div {
  -webkit-user-select: none;
}

// 禁止长按呼出菜单
div {
  -webkit-touch-callout: none;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 7. 使滚动丝滑

div {
  overflow: auto | scroll;
  /* 只要是有滚动天的地方都可以加这一行 */
  -webkit-overflow-scrolling: touch;
}
1
2
3
4
5

# 8. 屏幕旋转为横屏的时候,字体大小可能变化

* {
  -webkit-text-size-adjust: 100%;
}
1
2
3

# 9. 简单的 rem 自适应

html {
  font-size: calc(100vw / 3.75);
}

body {
  font-size: 0.14rem;
}
1
2
3
4
5
6
7

# 10. 滑动穿透

<div class="mask">
  <div class="content">我是弹框</div>
</div>
1
2
3
.mask {
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  background-color: rgba($color: #333, $alpha: 0.6);

  .content {
    padding: 20px;
    background-color: #fff;
    width: 300px;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

阻止父节点的默认事件

document.querySelector(".mask").addEventListener("touchmove", (event) => {
  event.preventDefault();
});
1
2
3
<div class="mask" @touchumove.prevent></div>
1

如果.content 也有滚动条,那么只要阻止遮罩本身就行:

document.querySelector(".mask").addEventListener("touchmove", (event) => {
  if (event.target.classList.contains("mask")) event.preventDefault();
});
1
2
3
<div class="mask" @touchumove.self.prevent></div>
1

# 11. 适配问题

# ① 设备独立像素

开发过移动端项目的前端,应该对 chrome 的调试工具不陌生的吧

图解

设备独立像素也可以理解为 CSS 像素,也称为逻辑像素(设备独立像素 = CSS 像素 = 逻辑像素)

# ② 物理像素

我们平常说的分辨率是2436*1125这里描述的屏幕实际的物理像素,又被成为设备像素(物理像素 = 设备像素)

物理像素:显示屏是由一个个像素点组成的2436*1125表示手机分别在纵向和横向上面说具备的像素点。通过控制每一个像素点的颜色来,就可以使屏幕显示不同的图像, 这个数据只要从手机做好的那一刻起,就不会再变化,单位是pt

# ③ 设备像素比(DPR)

说到这个的时候前端小伙伴就非常的了解,其实必须要由上面的两个概念,才会有DPR这个概念,因为设备像素比是设备独立像素和物理像素算出来的

DPR = 物理像素 / 设备独立像素

视网膜屏幕是苹果公司的一个营销术语,他们将dpr > 1的屏幕称为视网膜屏幕

对比

从上面的图中可以明确的看出,当 dpr=2 时候,把 4(2*2)当 1 个像素用,这样屏幕肯定会更加清晰的,但是元素本身并没有改变,如下图:

例子

到这个地方,大致做 H5 的是适配可以分成两个方面:

  1. 适配不同屏幕大小,也就是视频不同屏幕的 CSS 像素

  2. 适配不同像素密度,也就是适配不同屏幕下 dpr 不一致的一些问题

# ④ 适配不同屏幕

# 1. 使用百分比

开始前端布局上面很多人使用的都是百分比布局,所有的长宽都使用百分比完成,这样再布局上是可以做到的,但是百分比有一个问题就是我们都知道百分比是需要一个参照基准值的,但是在 CSS 中,

百分比值总要相对于另一个量,比如长度。每个允许使用百分比值的属性,同时也要定义百分比值参照的那个量。这个量可以是相同元素的另一个属性的值,也可以是祖先元素的某个属性的值,甚至是格式化上下文的一个度量(比如包含块的宽度)。 具体一点可以看:

  • 宽度(width)、边距(padding、margin)、支持百分比,但是默认的相对参考值是包含块的宽度
  • 高度(height)百分比的大小是相对父级像素的高
  • 边框(border)不支持百分值;
  • 边框圆角半径(border-radius)支持百分比值,但水平方向相对参考值是盒子的宽度,垂直方向相对参考值是盒子的高度;
  • 文本大小(font-size)支持百分比值,但相对参考值是父元素的 font-size 的值;
  • 盒阴影(box-shadow)和文本阴影(text-shadow)不支持百分比值;

这么多的地方,有可想而知的一些问题,以及使用起来麻烦

# 2. rem 适配方案

在 vw 出来之前,rem 用的是最多的,当然那时候我用的也是 rem,因为确实很好用的,rem 满足了百分比的参照基准值一致的不足

rem 根据网页的根元素来设置字体大小,和 em(font size of the element)的区别是,em 是根据其父元素的字体大小来设置,而 rem 是根据网页的跟元素(html)来设置字体大小。

典型的就有淘宝出品的lib-flexible (opens new window),现在已经有一些改变,还有我之前我没有使用的hotcss (opens new window),看了一下这个的思想和 flexible 是一样的

rem 虽好,可不要贪杯,它也是有一些问题的:

  1. 动态修改 Viewport 存在一定的风险的,譬如通过 Viewport 改变了页面的缩放之后,获取到的 innerWidth/innerHeight 也会随之发生变化,如果业务逻辑有获取此类高宽进行其他计算的,可能会导致意想不到的错误;

  2. flexible/hotcss 都并非纯 CSS 方案,需要引入一定的 Javascript 代码

  3. rem 的设计初衷并非是用于解决此类问题,用 rem 进行页面的宽度适配多少有一种 hack(简单说一下,就是通过加前缀来适配不同内核的浏览器) 的感觉

  4. 存在一定的兼容性问题,对于安卓 4.4 以下版本系统不支持 viewport 缩放(当然,flexible 处理 Android 系列时,始终认为其 dpr 为 1,没有进行 viewport 缩放)

# 3. vw 适配方案

严格来说,使用 rem 进行页面适配其实是一种 hack 手段,rem 单位的初衷本身并不是用来进行移动端页面宽度适配的。

百分比适配方案的核心需要一个全局通用的基准单位,rem 是不错,但是需要借助 Javascript 进行动态修改根元素的 font-size,而 vw/vh(vmax/vmin) 的出现则很好弥补 rem 需要 JS 辅助的缺点。

根据 CSS Values and Units Module Level 4:vw等于初始包含块(html 元素)宽度的 1%,也就是

  • 1vw 等于 window.innerWidth 的数值的 1%

  • 1vh 等于 window.innerHeight 的数值的 1%

# 自动转换

① 书写时候的转化

当我们使用 rem 作为长度单位的时,通常会有借助 Sass/Less 实现一个转换函数,像是这样:

// 假设设计稿的宽度是 375px(从设计稿量出来的)
$baseFontSize: 37.5px;
@function px2rem($px) {
  @return $px / $baseFontSize * 1rem;
}
div {
  width: px2rem(100);
}
1
2
3
4
5
6
7
8

同理,在 vw 方案下,我们只需要去改写这个方法

// 假设设计稿的宽度是 375px
@function px2vw($px) {
  @return $px / 375 * 100vw;
}

div {
  width: px2vw(100);
}
1
2
3
4
5
6
7
8

② 使用插件转化

postcss-px-to-viewport (opens new window)

放做完的一个 H5 的项目使用的vant (opens new window)使用的就是 vw

# 12. IScroll 中因为 click 配置而影响两端的不同的问题

在使用 IScroll:click 为 false 的时候在 Android 上面点击不生效,click 为 true 的时候在 IOS 上面需要双击才生效

添加设备判断,给 click 的值设为动态的解决此问题

# 13. 调用图片会会有图片旋转的问题出现

这样的问题不是只有在 H5 上面会出现,上传图片如果使用的是手机照的图片等,都可能出现图片旋转的问题

原因是:

EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照片而定制的元数据,可以记录数码照片的拍摄参数、缩略图及其他属性信息。 图像一旦被修改,Exif 信息可能会永久丢失,故编辑 Exif 必须使用专门的软件。

解决方案:git 地址 (opens new window)

# 14. H5 点击的时候有小灰块背景

想要出黑色小灰块,添加 css

// 去除小灰快
html,
body {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
1
2
3
4
5
6

# 15. 图片预览的物理键返回问题

今天写了一个简单的全屏图片预览组件,想到了一个问题,预览我给的是一个全屏的popup,如果是正常的点击关闭,当然是没有问题,但是有一个要幺蛾子就是在安卓机上面的物理键返回,我的对物理键的理解是window.history.go(-1),有几点要求

  1. 关闭放大的问题,回到打开前的页面
  2. 页面不能刷新,因为在放大预览之前可能,页面可能被编辑过了

最后使用的方式是:

在进入预览页面的时候改变一下 href,location.href = location.href + '&xx=11;'

非物理键出组件的时候window.history.go(-1),模拟一次物理键的返回

# 16. ios 上面添加地图定位后图片不不加载的问题

前两天一小伙伴遇到的,在她的项目中,遇到一个图片在 ios 上加载不出来的问题,最后经过一顿排查之后,使用为使用高德地图之后,在 ios 上面只能经行 https 请求图片 修改方案:

  1. 在不使用高德地图的时候:销毁实例(有待她的验证)
  2. 将图片上传到 https 协议的服务器,可以请求

在 IOS 中 webview 中navigator.geolocation.getCurrentPosition在非完全 http 站点(网站有 http 请求的图片)失效

参考链接:WebKit on iOS ignores trigger(‘click’) on file input (opens new window)

# 17. faskclick 在 ios 上面输入框聚焦慢或者无法聚焦的问题

为了解决移动端上我们为了解决点击 300ms 的言辞,常常会在项目中添加 fastclick,但是添加完之后,在 IOS 会遇到点击不聚焦,或者需要双击才能聚焦,解决方案添加一下代码就可以

// 解决IOS上聚焦慢的问题
FastClick.prototype.focus = function (targetElement: any) {
  targetElement.focus();
};
1
2
3
4

# 18. input 获取焦点, 页面放大, 失焦后,页面未恢复

<meta name="apple-mobile-web-app-capable" content="yes" />
<meta
  name="viewport"
  content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"
/>
1
2
3
4
5

# 19. ios 输入框英文首字母的默认大写的取消

<input autocapitalize="off" autocorrect="off" />
1

# 20. 某些 Android 手机圆角失效

background-clip: padding-box;
1

# 21. 改变输入框 placeholder 的颜色值

::-webkit-input-placeholder {
  color: #999;
}
input:focus::-webkit-input-placeholder {
  color: #999;
}
1
2
3
4
5
6

# 22. 输入框被键盘挡住问题

window.addEventListener("resize", function () {
  if (
    document.activeElement.tagName === "INPUT" ||
    document.activeElement.tagName === "TEXTAREA" // 获得文档中获取焦点的元素
  ) {
    window.setTimeout(function () {
      if ("scrollIntoView" in document.activeElement) {
        document.activeElement.scrollIntoView(); // 滚动当前元素到可见区域
      } else {
        document.activeElement.scrollIntoViewIfNeeded();
      }
    }, 0);
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

备注: 这个问题现在很多的手机中都不存在了

问题的来由: 在开发财务报销的项目的时候,测试在 ios12 的时候反馈说底部 footer 无法触底,开始我还以为是进入页面就会出现的问题,一顿查怎么都复显不了,结果不是,实在文本编辑调出键盘时候,页面没有回弹导致

这个问题在之前就有记录过

在调试的过程中发现一个其他的问题:当键盘被调起的时候,页面的滚动比较乱,因为我自己喜欢使用display: flex + flex: 1的方式做 footer 布局,这样就容易有两个滚动条,而且滚动条很难滚动到 footer 部分,想改成 fixed 布局的,但是那样的改变太大,最后和产品商量当页面滚动的时候收起键盘,没想到效果还挺好,使用起来也好挺舒服,有点像微信的聊天的时候的交互

注: 有些时候的有些交互,要善于和产品、设计沟通,有可能可以让自己少做一些事情,而且得到的效果是比之前好的,不要做只顾着低头开发

# 24. 字体大小小于 12px 时候的垂直居中

在部分安卓机上面字体小于 12px 的时候,使用常规的垂直居中字体会偏上一点

原因:

这个问题通过 css 是无法解决的,即使解决了也是一种通过微调来实现的 hack 方法,因为文字在 content-area 内部渲染的时候已经偏移了,而 css 的居中方案都是控制的整个 content-area 的居中。导致这个问题的本质原因可能是 Android 在排版计算的时候参考了 primyfont 字体的相关属性(即 HHead Ascent、HHead Descent 等),而 primyfont 的查找是看font-family里哪个字体在 fonts.xml 里第一个匹配上,而原生 Android 下中文字体是没有 family name 的,导致匹配上的始终不是中文字体,所以解决这个问题就要在font-family里显式申明中文,或者通过什么方法保证所有字符都 fallback 到中文字体。根据这 2 个思路,目前我找到了 2 个解决方案:

  1. 针对 Android 7.0+设备:<html>上设置 lang 属性:<html lang="zh-cmn-Hans">,同时 font-family 不指定英文,如 font-family: sans-serif 。这个方法是利用了浏览器的字体 fallback 机制,让英文也使用中文字体来展示,blink 早期的内核在 fallback 机制上存在问题,Android 7.0+才能 ok,早期的内核下会导致英文 fallback 到 Noto Sans Myanmar,这个字体非常丑。
  2. 针对 MIUI 8.0+设备:设置 font-family: miui 。这个方案就是显式申明中文的方案,MIUI 在 8.0+上内置了小米兰亭,同时在 fonts.xml 里给这个字体指定了 family name:miui,所以我们可以直接设置。

# 25. 千分符的问题

在移动端开发的时候需要给数字添加千分符号,开始直接使用的是toLocaleString,结果在部分 android 手机上直接现实空白,特别是华为系列的,最后没办法,使用正则解决了

const addThousandthSign = (numStr) => {
  const regForm = /(\d{1,3})(?=(\d{3})+(?:$|\.))/g;
  return numStr.toString().replace(regForm, "$1,");
};
1
2
3
4

# 26. border-radius在子元素有transform时候overflow:hidden失效

在现在的小程序开发中,在电脑上面的模拟器上面是没有问题的,但是在部分手机上面发现父元素的overflow:hidden没有包住远圆角外的内容

解决方案: 给父元素添加transform rotate(0deg)

Last update: December 23, 2022 13:14
Contributors: liaoxuan , salvatoreliaoxuan , 廖轩