# 移动端H5填坑

# HTML

  • 调用系统功能

    <!-- 拨打电话 -->
    <a href="tel:110">拨打电话给110</a>
    
    <!-- 发送短信 -->
    <a href="sms:110">发送短信给110</a>
    
    <!-- 发送邮件 -->
    <a href="mailto:xxj95719@gmail.com">发送邮件</a>
    
    <!-- 选择照片或拍摄照片 -->
    <input type="file" accept="image/*">
    
    <!-- 选择视频或拍摄视频 -->
    <input type="file" accept="video/*">
    
    <!-- 多选文件 -->
    <input type="file" multiple>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • 忽略自动识别

    <!-- 忽略自动识别电话 -->
    <meta name="format-detection" content="telephone=no">
    
    <!-- 忽略自动识别邮箱 -->
    <meta name="format-detection" content="email=no">
    
    <!-- 忽略自动识别电话和邮箱 -->
    <meta name="format-detection" content="telephone=no, email=no">
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 弹出数字键盘

    <!-- 纯数字带#和* -->
    <input type="tel">
    
    <!-- 纯数字 -->
    <input pattern="\d*">
    
    1
    2
    3
    4
    5
  • 唤醒原生应用

    <!-- 打开微信 -->
    <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

    查看更多参考:《H5与App的通讯方式》

  • 禁止页面缩放

    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
    
    1
  • 禁止页面缓存

    <meta http-equiv="Cache-Control" content="no-cache">
    
    
    1
    2
  • 禁止字母大写

    声明autocapitalize=off关闭首字母大写功能和autocorrect=off关闭纠正功能

    <input autocapitalize="off" autocorrect="off">
    
    
    1
    2
  • 针对Safari配置

    <!-- 设置Safari全屏,在iOS7+无效 -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    
    <!-- 改变Safari状态栏样式,可选default/black/black-translucent,需在上述全屏模式下才有效 -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    
    <!-- 添加页面启动占位图 -->
    <link rel="apple-touch-startup-image" href="pig.jpg" media="(device-width: 375px)">
    
    <!-- 保存网站到桌面时添加图标 -->
    <link rel="apple-touch-icon" sizes="76x76" href="pig.jpg">
    
    <!-- 保存网站到桌面时添加图标且清除默认光泽 -->
    <link rel="apple-touch-icon-precomposed" href="pig.jpg">
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 针对其他浏览器配置

    <!-- 强制QQ浏览器竖屏 -->
    <meta name="x5-orientation" content="portrait">
    
    <!-- 强制QQ浏览器全屏 -->
    <meta name="x5-fullscreen" content="true">
    
    <!-- 开启QQ浏览器应用模式 -->
    <meta name="x5-page-mode" content="app">
    
    <!-- 强制UC浏览器竖屏 -->
    <meta name="screen-orientation" content="portrait">
    
    <!-- 强制UC浏览器全屏 -->
    <meta name="full-screen" content="yes">
    
    <!-- 开启UC浏览器应用模式 -->
    <meta name="browsermode" content="application">
    
    <!-- 开启360浏览器极速模式 -->
    <meta name="renderer" content="webkit">
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# CSS

  • IOS滚动卡顿

    .el {
      -webkit-overflow-scrolling: touch;
    }
    
    1
    2
    3
  • 禁止滚动传播

    桌面端浏览器不一样,移动端浏览器有一个奇怪行为。当页面包含多个滚动区域时,滚完一个区域后若还存在滚动动量则会将这些剩余动量传播到下一个滚动区域,造成该区域也滚动起来。这种行为称为滚动传播。

    .el {
      overscroll-behavior: contain;
    }
    
    1
    2
    3
  • 禁止屏幕抖动

    对于一些突然出现滚动条的页面,可能会产生左右抖动的不良影响。在一个滚动容器里,打开弹窗就隐藏滚动条,关闭弹窗就显示滚动条,来回操作会让屏幕抖动起来。提前声明滚动容器的padding-right为滚动条宽度,就能有效消除这个不良影响。

    每个移动端浏览器的滚动条宽度都有可能不一致,甚至不一定占位置,通过以下方式能间接计算出滚动条的宽度。100vw为视窗宽度,100%为滚动容器内容宽度,相减就是滚动条宽度,妥妥的动态计算。

    body {
        padding-right: calc(100vw - 100%);
    }  
    
    1
    2
    3
  • 禁止长按操作

    * {
        /* pointer-events: none; */ /* 微信浏览器还需附加该属性才有效 */
        user-select: none; /* 禁止长按选择文字 */
        -webkit-touch-callout: none;
    }
    
    1
    2
    3
    4
    5

    但声明user-select:none会让<input><textarea>无法输入文本,可对其声明user-select:auto排除在外。

    input,
    textarea {
        user-select: auto;
    }
    
    1
    2
    3
    4
  • 禁止字体调整

    * {
        text-size-adjust: 100%;
    }
    
    1
    2
    3
  • 禁止高亮显示

    触摸元素会出现半透明灰色遮罩

    * {
        -webkit-tap-highlight-color: transparent;
    }
    
    
    1
    2
    3
    4
  • 禁止动画闪屏

    在移动设备上添加动画,多数情况会出现闪屏,给动画元素的父元素构造一个3D环境就能让动画稳定运行了

    .father {
        perspective: 1000;
        backface-visibility: hidden;
        transform-style: preserve-3d;
    }
    
    
    1
    2
    3
    4
    5
    6
  • 自定义表单元素

    button,
    input,
    select,
    textarea {
        appearance: none;
        /* 自定义样式 */
    }
    
    1
    2
    3
    4
    5
    6
    7
  • 自定义滚动条

    /* 滚动条整体部分 */
    ::-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(135deg, #09f, #3c9);
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • 自定义placeholder

    input::-webkit-input-placeholder {
        color: #66f;
    }
    
    1
    2
    3
  • 居中对齐输入占位placeholder

    input {
      line-height: normal;
    }
    
    
    1
    2
    3
    4
  • 对齐下拉选项

    /* 默认左对齐:ltr */
    select option {
      direction: rtl; /* 右对齐 */
    }
    
    
    1
    2
    3
    4
    5
  • 修复IOS点击无效

    .el {
      cursor: pointer;
    }
    
    1
    2
    3
  • 识别文本换行

    可识别文本中的\n<br>,并自动换行

    .el {
      white-space: pre-line;
    }
    
    1
    2
    3
  • 开启GPU硬件加速

    .el {
      transform: translate3d(0, 0, 0);
    }
    
    1
    2
    3
  • 1px边框

    .el {
      position: relative;
      width: 200px;
      height: 80px;
      &::after {
          position: absolute;
          left: 0;
          top: 0;
          border: 1px solid #f66;
          width: 200%;
          height: 200%;
          content: "";
          transform: scale(.5);
          transform-origin: left top;
      }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • 文本溢出显示省略号

    .el {
      width: 400px;
      line-height: 30px;
      font-size: 20px;
      &.sl-ellipsis {
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
      }
      &.ml-ellipsis {
          display: -webkit-box;
          overflow: hidden;
          text-overflow: ellipsis;
          -webkit-line-clamp: 3;
          -webkit-box-orient: vertical;
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# JS

  • 禁止点击穿透

    fastclick

  • 禁止滑动穿透

    问题:移动端浏览器里出现弹窗时,若在屏幕上滑动能触发弹窗底下的内容跟着滚动。

    期望:除了弹窗内容能点击或滚动,其他内容都不能点击或滚动

    方案: 当打开弹窗时给<body>声明position:fixed;left:0;width:100%并动态声明top。声明position:fixed会导致<body>滚动条消失,此时会发现虽然无滑动穿透,但页面滚动位置早已丢失。通过scrollingElement获取页面当前滚动条偏移量并将其取负值且赋值给top,那么在视觉上就无任何变化。当关闭弹窗时移除position:fixed;left:0;width:100%和动态top

    body.static {
        position: fixed;
        left: 0;
        width: 100%;
    }
    
    1
    2
    3
    4
    5
    const body = document.body;
    const openBtn = document.getElementById("open-btn");
    const closeBtn = document.getElementById("close-btn");
    openBtn.addEventListener("click", e => {
        e.stopPropagation();
        const scrollTop = document.scrollingElement.scrollTop;
        body.classList.add("static");
        body.style.top = `-${scrollTop}px`;
    });
    closeBtn.addEventListener("click", e => {
        e.stopPropagation();
        body.classList.remove("static");
        body.style.top = "";
    });
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 支持往返刷新

    // 在新页面监听页面销毁事件
    window.addEventListener("onunload", () => {
        // 执行旧页面代码
    });
    
    1
    2
    3
    4
    • pageshow事件在每次页面加载时都会触发,无论是首次加载还是再次加载都会触发,这就是它与load事件的区别。pageshow事件暴露的persisted可判断页面是否从BFCache里取出,前提条件: 浏览器不使用<meta http-equiv="Cache-Control" content="no-cache">禁用缓存
    window.addEventListener("pageshow", e => e.persisted && location.reload());
    
    
    1
    2
  • IOS时间格式解析

    在苹果系统上解析YYYY-MM-DD HH:mm:ss这种日期格式会报错Invalid Date,但在安卓系统上解析这种日期格式完全无问题。

    const date = "2021-01-26 09:30:00";
    new Date(date.replace(/\-/g, "-"));
    
    1
    2
  • 修复高度坍塌

    当页面同时出现以下三个条件时,键盘占位会把页面高度压缩一部分。当输入完成键盘占位消失后,页面高度有可能回不到原来高度,产生坍塌导致Webview底色露脸,简单概括就是输入框失焦后页面未回弹

    • 页面高度过小
    • 输入框在页面底部或视窗中下方
    • 输入框聚焦输入文本

    只要保持前后滚动条偏移量一致就不会出现上述问题。在输入框聚焦时获取页面当前滚动条偏移量,在输入框失焦时赋值页面之前获取的滚动条偏移量,这样就能间接还原页面滚动条偏移量解决页面高度坍塌。

    const input = document.getElementById("input");
    let scrollTop = 0;
    input.addEventListener("focus", () => {
        scrollTop = document.scrollingElement.scrollTop;
    });
    input.addEventListener("blur", () => {
        document.scrollingElement.scrollTo(0, this.scrollTop);
    });
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 回到顶部

    const gotopBtn = document.getElementById("gotop-btn");
    openBtn.addEventListener("click", () => document.body.scrollIntoView({ behavior: "smooth" }));
    
    
    1
    2
    3
  • 自动播放媒体

    常见媒体元素包括音频<audio>和视频<video>,为了让用户得到更好的媒体播放体验与不盲目浪费用户流量,大部分移动端浏览器都明确规定不能自动播放媒体或默认屏蔽autoplay。为了能让媒体在页面加载完成后自动播放,只能显式声明播放。

    const audio = document.getElementById("audio");
    const video = document.getElementById("video");
    audio.play();
    video.play();
    
    
    1
    2
    3
    4
    5

    对于像微信浏览器这样的内置浏览器,还需监听其应用SDK加载完成才能触发上述代码,以保障WebView正常渲染。其他内置浏览器同理。

    document.addEventListener("WeixinJSBridgeReady", () => {
      // 执行上述媒体自动播放代码
    });
    
    
    1
    2
    3
    4

    在苹果系统上明确规定用户交互操作开始后才能播放媒体,未得到用户响应会被Safari自动拦截,因此需监听用户首次触摸操作并触发媒体自动播放,而该监听仅此一次。

    document.body.addEventListener("touchstart", () => {
        // 执行上述媒体自动播放代码
    }, { once: true });
    
    
    1
    2
    3
    4

# 调试工具

  • Weinre远程调试工具

    - 本地全局安装weinre , 命令行:npm install -g weinre
    - 在本地启动一个检测器:weinre --boundHost 10.253.100.131IP为本地IP地址)
    - 在浏览器访问此地址:http://10.253.100.131:8080
    - 把下面这一串东西,放在你需要调试的页面里:
    <script src="http://10.253.100.131:8080/target/target-script-min.js#anonymous"></script>
    - 点击链接打开:http://10.253.100.131:8080/client/#anonymous
    
    
    1
    2
    3
    4
    5
    6
    7
  • vconsole 日志打印

    import VConsole from 'vconsole'
    var vConsole = new VConsole();
    
    1
    2