H5 移動端 鍵盤遮擋焦點元素解決方案

前言


最近在作 webapp,遇到了不少移動端兼容的問題,其中一個問題就是:輸入框觸發 focus 後,鍵盤彈出,而後遮住了輸入框。css

而後在AndroidIOS上,這個問題的表現形式不同,而原生鍵盤和第三方鍵盤也不同,但引發的問題都是同樣的:輸入框被遮住了。html

須要的效果


在鍵盤彈出時,得到焦點的輸入框要在可視區域內,效果以下圖:android

鍵盤彈出、收起的表現


  1. IOS:web

    輸入框獲取焦點,鍵盤彈出,webview高度不會改變,但webview會往上滾,且最大滾動高度scrollTop爲鍵盤高度。瀏覽器

    點擊鍵盤上的收起按鈕,或者輸入框之外的頁面區域時,輸入框失去焦點,鍵盤收起。app

  2. Android:webapp

    輸入框獲取焦點,鍵盤彈出,可是webview高度會發生改變,高度爲原高度減去軟鍵盤高度。iphone

    點擊輸入框之外的區域時,輸入框失去焦點,軟鍵盤收起。而點擊鍵盤上的收起按鈕時,鍵盤收起 ,但輸入框並不會失去焦點,測試

## 解決方案ui


當輸入框位於頁面下部位置時,在IOS中,webview會往上滾一段距離,使得獲取焦點的輸入框自動處於可視區,而在Android裏,只會改變頁面高度,而不會發生焦點元素滾動到可視區的事情。

因此IOS能夠不用管,而Android須要在鍵盤彈出的時候,將輸入框滾動到可視區。

獲取設備類型


首先是要獲取設備類型,經過navigator.userAgent獲取便可。

function judgeDeviceType() {
  let deviceType = null;

  if (!deviceType) {
    const ua = window.navigator.userAgent.toLocaleLowerCase();
    const isIOS = /iphone|ipad|ipod/.test(ua);
    const isAndroid = /android/.test(ua);

    deviceType = {
      isIOS: isIOS,
      isAndroid: isAndroid
    };
  }

  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。

備註


解決方案並不完善,踩坑路漫漫。

相關文章
相關標籤/搜索