ios12、ios13系统的微信浏览器在软键盘收起时,页面无法回滚的bug修复

2,730 阅读2分钟

修改记录:

  • 2019.10.12 发现ios13存在同样问题,增加了想要修复代码
  • 2020.3.3 自定义 vue directive

Vue-directive解决方案传送们

bug如题,图片如下:

有bug的解决方案

在输入框上监听blur事件,事件触发后将body滚动至底部。

functiont scrollTop () {
    // ios12有效
    window.document.body.scrollTop = window.document.body.scrollHeight;
    // ios13有效 2019.10.12增加
    window.document.documentElement.scrollTop = window.document.body.scrollHeight;
}
// el是输入框
el.onblur = scrollTop;

新写了两bug

  • bug1: 点击“提交留言”按钮,虽然软键盘收起后页面回滚至底部,但是提交事件没有触发
  • bug2: 在多个输入框间切换输入,页面滚动凌乱了,页面先滚至底部又滚回至输入框在可是区域

解决新bug1

原因:

  • 输入框blur事件发生在其他事件之前
  • 事件回调是同步执行
  • 页面滚动了,导致页面的point点坐标不在“提交留言”按钮的位置
  • 无法触发“提交留言”按钮的点击事件

解决:

// el是输入框
el.onblur = () => {
    setTimeout(scrollTop, 0);
};

解决新bug2

原因:

  • 切换输入框后,js代码将body滚动至底部,这是同步的。
  • 切换输入框后,webview会将新获取焦点输入框滚动至可视区域,这是异步的。
  • bug1的解决方案将输入框失去焦点的回调改成异步,没有解决bug2的问题,
    问题变成:在多个输入框间切换输入,webview因输入框切换将获取焦点输入框滚动至可视区域,但是js代码异步将页面滚动至底部,有时导致新获取焦点输入框不在可是范围。

解决: 在输入框获取和失去焦点时,clearTimeout

doms.forEach((item) => {
  item.onfocus = () => {
    // 元素获取焦点时,由webview滚动元素至可是区域
    window.inputFocuseTimeout && clearTimeout(window.inputFocuseTimeout);
  };
  item.onblur = () => {
    window.inputFocuseTimeout && clearTimeout(window.inputFocuseTimeout);
    window.inputFocuseTimeout = setTimeout(scrollTop, 0);
  };
});

vue directive方案

注册全局directive

import Vue from 'vue'
// 移動端blur后跳轉至頁面最下方
Vue.directive('polyfill-input', {
  bind: function (el) {
    el.onblur = e => {
      window.inputFocuseTimeout && clearTimeout(window.inputFocuseTimeout)
      window.inputFocuseTimeout = setTimeout(() => {
        if (/\(i[^;]+;( U;)? CPU.+Mac OS X/.test(navigator.userAgent)) {
          // ios12
          window.document.body.scrollTop = window.document.body.scrollHeight
          // ios13
          window.document.documentElement.scrollTop = window.document.body.scrollHeight
        }
      }, 0)
    }
    el.onfocus = e => {
      window.inputFocuseTimeout && clearTimeout(window.inputFocuseTimeout)
    }
  },
  unbind: function (el) {
    el.onblur = null
    el.onfocus = null
  }
})

使用范围:input,textarea标签

<input type="text" v-polyfill-input/>
<textarea v-polyfill-input></textarea>

推荐页面结构

长页面使用以上方案出现页面总是被滚动到底部,因为方案解决问题的中心点就是将body滚动至底部,所以推荐以下页面结构。让#app成为可滚动元素。

<html>
    <body>
        <div id="#app">
            ...
        </div>
    </dody>
</html>
<style>
html, body, #app {
    width: 100%;
    height: 100%;
}
html, body {
    overflow: hidden;
}
</style>

结语

菜鸟第一次写东西,很脆弱的。所以有问题请留言问题,我积极修改。没有问题,请留言鼓励。