欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

H5 移动端 键盘遮挡焦点元素解决方案

程序员文章站 2022-05-18 20:50:34
前言 最近在做 webapp,遇到了很多移动端兼容的问题,其中一个问题就是:输入框触发 focus 后,键盘弹出,然后遮住了输入框。 然后在 和`IOS`上,这个问题的表现形式不一样,而原生键盘和第三方键盘也不一样,但引起的问题都是一样的:输入框被遮住了。 需要的效果 在键盘弹出时,获得焦点的输入框 ......

前言


最近在做 webapp,遇到了很多移动端兼容的问题,其中一个问题就是:输入框触发 focus 后,键盘弹出,然后遮住了输入框。

然后在androidios上,这个问题的表现形式不一样,而原生键盘和第三方键盘也不一样,但引起的问题都是一样的:输入框被遮住了。

需要的效果


在键盘弹出时,获得焦点的输入框要在可视区域内,效果如下图:

H5 移动端 键盘遮挡焦点元素解决方案

键盘弹出、收起的表现


  1. ios:

    输入框获取焦点,键盘弹出,webview高度不会改变,但webview会往上滚,且最大滚动高度scrolltop为键盘高度。

    点击键盘上的收起按钮,或者输入框以外的页面区域时,输入框失去焦点,键盘收起。

  2. android:

    输入框获取焦点,键盘弹出,但是webview高度会发生改变,高度为原高度减去软键盘高度。

    点击输入框以外的区域时,输入框失去焦点,软键盘收起。而点击键盘上的收起按钮时,键盘收起 ,但输入框并不会失去焦点,

解决方案


当输入框被挡住,在ios中,webview会往上滚一段距离,使得获取焦点的输入框自动处于可视区,而在android里,只会改变webview高度,而不会发生焦点元素滚动到可视区的事情。

所以ios可以不用管,而android需要在键盘弹出的时候,将输入框滚动到可视区。

获取设备类型


首先是要获取设备类型,通过navigator.useragent获取即可。

const judgedevicetype = (() => {
  let devicetype = null;

  return () => {
    if (!devicetype) {
      const ua = window.navigator.useragent.tolocalelowercase();
      const isios = /iphone|ipad|ipod/.test(ua);
      const isandroid = /android/.test(ua);
      const ismiuibrowser = /miuibrowser/.test(ua);

      devicetype = {
        isios: isios,
        isandroid: isandroid,
        ismiuibrowser: ismiuibrowser
      };
    }

    return devicetype;
  };
})();

监听事件


ios 可以通过focusblur事件监听键盘弹出、收起,但 android 不行,但因为webview高度会变,所以通过监听resize事件解决。

export function listenandroidkeybord() {
  const { isandroid } = judgedevicetype();

  if (isandroid) {
    const androidresize = function() {
      // 将当前焦点元素滚动到可视区
      activeelementscrollintoview();
    };

    // android 键盘弹出、收起,可视区高度会发生变化
    window.addeventlistener('resize', androidresize, false);

    return () => {
      window.removeeventlistener('resize', androidresize, false);
    };
  }
}

将元素滚动到可视区


要将元素滚动到可视区,主要有两个方法:scrollintoviewscrollintoviewifneeded,兼容性在移动端都很不错。

function activeelementscrollintoview() {
  const activeel = document.activeelement;

  if (
    activeel.tagname === 'input' || 
    activeel.tagname === 'textarea'
  ) {
    window.settimeout(() => {
      if ('scrollintoview' in activeel) {
        activeelt.scrollintoview();
      } else {
        activeel.scrollintoviewifneeded();
      }
    }, 100);
  }
}

miuibrowser


以上代码可以说解决了大部分浏览器键盘遮挡问题了,但我用自己的小米手机自带的小米浏览器测试时,出了问题,键盘弹出,页面纹丝不动,手动去拖,有时行,有时不行。

搞了很久,发现了两个问题,我这手机上自带的小米浏览器,useragent 上没有带android标识,但有miuibrowser标识,。然后,页面有时能拖动,有时不能拖动,我猜应该是webview的可视区高度变化有问题,或者是我的代码监听resize导致有问题。

解决方案

  1. 增加设备类型判断

    const ua = window.navigator.useragent.tolocalelowercase();
    const ismiuibrowser = /miuibrowser/.test(ua);
  2. 通过监听focusblur事件来监听键盘弹出、收起,然后给body加高度

    body, html {
      height: 100%;
    }
    function listenmiuibrowserkeybord() {
      const { ismiuibrowser } = judgedevicetype();
    
      if (ismiuibrowser) {
        const inputfocus = function() {
          document.body.style.marginbottom = '50px';
          activeelementscrollintoview();
        };
    
        const inputblur = function() {
          document.body.style.marginbottom = '0px';
          activeelementscrollintoview();
        };
    
        let $inputs = document.getelementsbytagname('input');
        for (let i = 0; i < $inputs.length; i++) {
          $inputs[i].addeventlistener('focus', inputfocus, false);
          $inputs[i].addeventlistener('blur', inputblur, false);
        }
    
        return () => {
          for (let i = 0; i < $inputs.length; i++) {
            $inputs[i].removeeventlistener('focus', inputfocus, false);
            $inputs[i].removeeventlistener('blur', inputblur, false);
          }
        };
      }
    }

坑点:这种方案虽然解决了弹出问题,但点击键盘收起按钮,android 下输入框并不会失去焦点,需要失去焦点才能让 body 增加的高度变为 0。

备注


解决方案并不完善,踩坑路漫漫。