移动端开发技巧积累

4/26/2021 前端移动端

# 1px 的边框

element {
  position: relative;
  width: 200px;
  height: 80px;

  &::after {
    position: absolute;
    left: 0;
    top: 0;
    // 如果想要单边框,修改这里
    border: 1px solid #ccc;
    width: 200%;
    height: 200%;
    content: '';
    transform: scale(0.5);
    transform-origin: left top;
  }
}

# input 标签 type 属性使用

只罗列 IOS 和 Android 都支持的功能

  • 普通文本
<input type="text" />
  • 字母带@.,适合输入邮箱
<input type="email" />
  • 字母带/..com,适合输入网址
<input type="url" />
  • 纯数字带 #*,适合输入电话号码
<input type="tel" />
  • 纯数字,适合输入纯数字验证码
<input type="number" pattern="\d*" />
  • 选择年月日
<input type="date" />
  • 选择年月
<input type="month" />
  • 选择时间点,几点几分
<input type="time" />
  • 选择年月日时分秒
<input type="datetime-local" />

# 媒体查询监听屏幕旋转

/* 横屏 */
@media all and (orientation: landscape) {
  /* 自定义样式 */
}

/* 竖屏 */
@media all and (orientation: portrait) {
  /* 自定义样式 */
}

# 忽略 IOS 端页面中的自动识别

  • 忽略自动识别电话号码
<meta name="format-detection" content="telephone=no" />
  • 忽略自动识别邮箱
<meta name="format-detection" content="email=no" />
  • 禁止跳转至地图
<meta name="format-detection" content="address=no" />
  • 合并写法
<meta name="format-detection" content="telephone=no,email=no,address=no" />

# 使用 a 标签调用电话、短信、邮件

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

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

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

# IOS 端验证码自动填充两次问题

  • 使用 maxlength 限制输入位数
  • type 必须为 text
<input type="text" maxlength="6" />

# 去除 IOS 端按钮默认的渐变色

表单元素的 appearance 样式属性导致的,设置为 'none' 即可

button {
  -webkit-appearance: none;
  appearance: none;
}

# 使用 autocapitalize 控制输入框首字母自动大写行为

# 合法取值

  • off 或者 none: 没有应用自动大写(所有字母都默认为小写字母)。
  • on 或者 sentences: 每个句子的第一个字母默认为大写字母;所有其他字母都默认为小写字母。
  • words: 每个单词的第一个字母默认为大写字母;所有其他字母都默认为小写字母。
  • characters: 所有的字母都默认为大写。

在物理键盘上输入时,autocapitalize 属性不会影响行为。相反,它会影响其他输入机制的行为,比如移动设备的虚拟键盘和语音输入。这种机制的行为是,它们经常通过自动地将第一个句子的字母大写来帮助用户。autocapitalize 属性使作者能够覆盖每个元素的行为。

autocapitalize 属性永远不会为带有 type 属性,其值为 url, emailpassword<input> 元素启用自动大写。

# 示例

<input autocapitalize="off" />

# 禁用输入框的自动完成

<input autocomplete="off" />

# 禁用输入框的自动更正

<input autocorrect="off" />

# 禁止页面缓存

<meta http-equiv="cache-control" content="no-cache" />

# 优化 :active:hover 的样式效果

在移动端,有时候 :active 选择器会不起作用;有时候 :hover 选择器的效果,会在点击后一直保持,需要点击其他地方才能恢复。

这时,可以在 body 标签上注册一个空的 touchstart 事件解决这两个问题。

<body ontouchstart>
  ...
</body>

# 去除点击可点击元素时的高亮效果

在 IOS 端和部分 Android 端,当点击一个链接或者其它可点击元素时,会出现一个半透明遮罩或边框,可通过以下样式去除该效果:

* {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-modify: read-write-plaintext-only;
}

# safari 浏览器中元素点击事件失效问题

# 问题描述

当使用「事件委托」监听「目标元素」的 click 事件时,如果「代理元素」是 documentbody,并且「目标元素」是默认不可点击的元素,例如:divspan等,此时 click 事件会失效;如果「目标元素」是可点击的元素,例如:buttona,此时 click 事件依然有效。

这是 safari 浏览器的限制。不希望用户点击不可点击的元素。

# 解决方案

解决方法很多种,任选一种即可:

  • 直接把事件绑定在「目标元素」。缺点是如果「目标元素」是动态插入的,不适合这么做。
  • 给「目标元素」添加一个空的属性 onclick="",例如:<div class="target" onclick="">点我点我</div>
  • 「代理元素」是 documentbody 时,「目标元素」必须使用 a 或者 button
  • 「目标元素」是不可点击元素时,「代理元素」选择不是 documentbody 的任意一个「祖先节点」。
  • 给「目标元素」添加样式 cursor: pointer;

# 控制浏览器过度滚动时的表现

overscroll-behaviorCSS 属性是overscroll-behavior-xoverscroll-behavior-y` 属性的合并写法, 让你可以控制浏览器过度滚动时的表现——也就是滚动到边界。

# 合法取值

  • auto 默认效果
  • contain
  • 设置这个值后,默认的滚动边界行为不变(“触底”效果或者刷新),但是临近的滚动区域不会被滚动链影响到,比如对话框后方的页面不会滚动。
  • none
  • 临近滚动区域不受到滚动链影响,而且默认的滚动到边界的表现也被阻止。

# 阻止滚动传播到临近的滚动区域

element {
  overscroll-behavior: contain;
  /* x方向 */
  overscroll-behavior-x: contain;
  /* y方向 */
  overscroll-behavior-y: contain;
}

# 阻止默认的滚动到边界的效果

例如默认的下拉刷新的触底效果

element {
  overscroll-behavior: none;
  /* x方向 */
  overscroll-behavior-x: none;
  /* y方向 */
  overscroll-behavior-y: none;
}

# 滚动条突然出现时的屏幕抖动问题

对于一些可能会突然出现滚动条的页面,会产生左右抖动的不良影响。

比如在一个滚动容器里,打开弹窗就隐藏滚动条,关闭弹窗就显示滚动条,来回操作会让屏幕抖动起来。

声明滚动容器的 padding-right 为滚动条宽度,可以优化这个问题。

移动端浏览器的滚动条宽度不一,有个小技巧是: 100vw 为视窗宽度,body 元素的 100% 为滚动容器内容宽度,相减就是滚动条宽度。

body {
  padding-right: calc(100vw - 100%);
}

# IOS 端非 body 元素的滚动优化

在 IOS 端,非 body 元素的滚动操作可能会出现卡顿,Android 端不会出现该情况。

可以通过声明 overflow-scrolling: touch; 调用系统原生滚动事件优化弹性滚动,增加页面滚动的流畅度。

.scroll-wrapper {
  -webkit-overflow-scrolling: touch;
}

# 自动播放媒体

自动播放网页中的音频或视频,会给用户带来困扰或者不必要的流量消耗,所以 IOS 和 Android 大多数浏览器都会屏蔽 autoplay 属性,禁止自动播放,且禁止用户未与网页有交互操作时,使用 JS 播放媒体。

因此我们可以通过监听用户首次触摸时,播放媒体。

但是有一个问题值得思考,如果恰巧遇到了部分稀有的浏览器,不需要交互就可以播放视频呢?有什么方法可以不放过这些漏网之鱼呢?

答案是有的,目光回到 .play() 这个方法上,以下是 MDN 的说明 (opens new window)

HTMLMediaElement.play() 方法会尝试播放媒体。这个方法返回一个 Promise,当媒体成功开始播放时被解决(resolved)。当播放因为任何原因失败时,这个 promise 被拒绝(rejected)。

/**
 * 触发自动播放
 */
function emitAutoPlay(selector) {
  const targetMediaDoms = document.querySelectorAll(selector)
  const pmsArr = []
  targetMediaDoms.forEach(dom => {
    pmsArr.push(dom.play())
  })
  Promise.all(pmsArr).catch(() => {
    document.body.addEventListener(
      'touchstart',
      () => {
        targetMediaDoms.forEach(dom => {
          pmsArr.push(dom.play())
        })
      },
      { once: true }
    )
  })
}

emitAutoPlay('audio')
emitAutoPlay('video')

注意,如果是像微信浏览器、钉钉浏览器这样的内置浏览器,需要监听其对应的 SDK 加载完成之后,再触发自动播放,以保障 webview 正常渲染。例如:

wx.ready(() => {
  emitAutoPlay('audio')
  emitAutoPlay('video')
})

# IOS 端日期转换错误问题

可以看我另一篇博客进行解决。

# IOS 端软键盘收起时页面不回落,底部留白问题

同时满足以下三种情况时会出现该问题。

  • 页面高度过小
  • 输入框在页面底部或视窗中下方
  • 输入框获取到焦点

修复方案:在输入框获取焦点时,获取页面当前滚动条偏移量,在输入框失焦时赋值页面之前获取的滚动条偏移量,这样就能间接解决该问题。

const input = document.getElementById('input')
let scrollTop = 0
input.addEventListener('focus', () => {
  scrollTop = document.scrollingElement.scrollTop
})
input.addEventListener('blur', () => {
  document.scrollingElement.scrollTo(0, scrollTop)
})