h5手機鍵盤彈出收起的處理

本文首發(fā)于 hzzly的博客

原文鏈接:h5手機鍵盤彈出收起的處理

前言:前端時間也是應項目的需求開始了h5移動端的折騰之旅,在目前中臺的基礎上擴展了兩個ToC移動端項目,下面就是在h5移動端表單頁面鍵盤彈出收起兼容性的一些總結。

問題

在 h5 項目中,我們會經(jīng)常遇到一些表單頁面,在輸入框獲取焦點時,會自動觸發(fā)鍵盤彈起,而鍵盤彈出在 IOS 與 Android 的 webview 中表現(xiàn)并非一致,同時當我們主動觸發(fā)鍵盤收起時也同樣存在差異化。

鍵盤彈出

  • IOS:IOS系統(tǒng) 的鍵盤處在窗口的最上層,當鍵盤彈起時,webview 的高度 height 并沒有改變,只是 scrollTop 發(fā)生變化,頁面可以滾動。且頁面可以滾動的最大限度為彈出的鍵盤的高度,而只有鍵盤彈出時頁面恰好也滾動到最底部時,scrollTop 的變化值為鍵盤的高度,其他情況下則無法獲取。這就導致在 IOS 情況下難以獲取鍵盤的真實高度。
  • Android: 在Android系統(tǒng)中,鍵盤也是處在窗口的最上層,鍵盤彈起時,如果輸入框在靠近底部的話,就會被鍵盤擋住,只有你輸入的時候輸入框才會滾動到可視化區(qū)域。

鍵盤收起

  • IOS:觸發(fā)鍵盤上的按鈕收起鍵盤或者輸入框以外的頁面區(qū)域時,輸入框會失去焦點,因此會觸發(fā)輸入框的 blur 事件;當鍵盤收起時,頁面底部會出現(xiàn)一個空白區(qū)域,頁面會被頂起。
  • Android: 觸發(fā)鍵盤上的按鈕收起鍵盤時,輸入框并不會失去焦點,因此不會觸發(fā)頁面的 blur 事件;觸發(fā)輸入框以外的區(qū)域時,輸入框會失去焦點,觸發(fā)輸入框的 blur 事件。

期望的結果

針對不同系統(tǒng)觸發(fā)鍵盤彈出收起時的差異化,我們希望功能流暢的同時,盡量保持用戶體驗的一致性。

對癥下藥

上面我們理清了目前市面上兩大主要系統(tǒng)的差異性,接下來就需對癥下藥了。

在 h5 中目前沒有接口可以直接監(jiān)聽鍵盤事件,但我們可以通過分析鍵盤彈出、收起的觸發(fā)過程及表現(xiàn)形式,來判斷鍵盤是彈出還是收起的狀態(tài)。

  • 鍵盤彈出:輸入框獲取焦點時會自動觸發(fā)鍵盤的彈起動作,因此,我們可以監(jiān)聽 focusin 事件,在里面實現(xiàn)鍵盤彈出后所需的頁面邏輯。

  • 鍵盤收起:當觸發(fā)其他頁面區(qū)域收起鍵盤時,我們可以監(jiān)聽 focusout 事件,在里面實現(xiàn)鍵盤收起后所需的頁面邏輯。而在通過鍵盤按鈕收起鍵盤時在 ios 與 android 端存在差異化表現(xiàn),下面具體分析:

    • IOS:觸發(fā)了 focusout 事件,仍然通過該辦法監(jiān)聽。
    • Android:沒有觸發(fā) focusout 事件。在 android 中,鍵盤的狀態(tài)切換(彈出、收起)不僅和輸入框關聯(lián),同時還會影響到 webview 高度的變化,那我們就可以通過監(jiān)聽 webview height 的變化來判斷鍵盤是否收起。

系統(tǒng)判斷

在實踐中我們可以通過 userAgent 來判斷目前的系統(tǒng):

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

IOS 處理

let isReset = true; //是否歸位

this.focusinHandler = () => {
  isReset = false; //聚焦時鍵盤彈出,焦點在輸入框之間切換時,會先觸發(fā)上一個輸入框的失焦事件,再觸發(fā)下一個輸入框的聚焦事件
};

this.focusoutHandler = () => {
  isReset = true;
  setTimeout(() => {
    //當焦點在彈出層的輸入框之間切換時先不歸位
    if (isReset) {
        window.scroll(0, 0); //確定延時后沒有聚焦下一元素,是由收起鍵盤引起的失焦,則強制讓頁面歸位
    }
  }, 30);
};

document.body.addEventListener('focusin', this.focusinHandler);
document.body.addEventListener('focusout', this.focusoutHandler);

Android 處理

const originHeight = document.documentElement.clientHeight || document.body.clientHeight;

this.resizeHandler = () => {
  const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
  const activeElement = document.activeElement;
  if (resizeHeight < originHeight) {
    // 鍵盤彈起后邏輯
    if (activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA")) {
      setTimeout(()=>{
        activeElement.scrollIntoView({ block: 'center' });//焦點元素滾到可視區(qū)域的問題
      },0)
    }
  } else {
    // 鍵盤收起后邏輯
  }
};

window.addEventListener('resize', this.resizeHandler);

react 封裝

在 react 中我們可以寫一個類裝飾器來修飾表單組件。

類裝飾器:類裝飾器在類聲明之前被聲明(緊靠著類聲明)。 類裝飾器應用于類構造函數(shù),可以用來監(jiān)視,修改或替換類定義。

// keyboard.tsx
/*
 * @Description: 鍵盤處理裝飾器
 * @Author: hzzly
 * @LastEditors: hzzly
 * @Date: 2020-01-09 09:36:40
 * @LastEditTime: 2020-01-10 12:08:47
 */
import React, { Component } from 'react';

const keyboard = () => (WrappedComponent: any) =>
  class HOC extends Component {
    focusinHandler: (() => void) | undefined;
    focusoutHandler: (() => void) | undefined;
    resizeHandler: (() => void) | undefined;
    componentDidMount() {
      const ua = window.navigator.userAgent.toLocaleLowerCase();
      const isIOS = /iphone|ipad|ipod/.test(ua);
      const isAndroid = /android/.test(ua);
      if (isIOS) {
        // 上面 IOS 處理
        ...
      }
      if (isAndroid) {
        // 上面 Android 處理
        ...
      }
    }

    componentWillUnmount() {
      if (this.focusinHandler && this.focusoutHandler) {
        document.body.removeEventListener('focusin', this.focusinHandler);
        document.body.removeEventListener('focusout', this.focusoutHandler);
      }
      if (this.resizeHandler) {
        document.body.removeEventListener('resize', this.resizeHandler);
      }
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };

export default keyboard;

使用

// PersonForm.tsx
@keyboard()
class PersonForm extends PureComponent<{}, {}> {
  // 業(yè)務邏輯
  ...
}

export default PersonForm;
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本文轉載自wuwhs的segmentfault專欄 最近一段時間在做 H5 聊天項目,踩過其中一大坑:輸入框獲取焦...
    兔子不打地鼠打代碼閱讀 10,804評論 1 12
  • 可能這些是你想要的H5軟鍵盤兼容方案 前言 輸入框獲取焦點,軟鍵盤彈起,要求輸入框吸附(或頂)在輸入法框上。需求很...
    記住了_葉閱讀 829評論 0 2
  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的。 ??事件,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,715評論 1 11
  • 之前在做H5 項目,踩過其中一大坑:輸入框獲取焦點,軟鍵盤彈起,要求輸入框吸附(或頂)在輸入法框上。需求很明確,看...
    超級無敵可愛的閱讀 2,485評論 0 3
  • 是發(fā)梢?guī)Ч獾哪昙o。 一個簡單的黑發(fā)圈束起我的長發(fā), 收起, 在被打動的年紀被打動的 細微的心頭的雀躍。 沉淀,沉淀...
    徐青年閱讀 423評論 2 10

友情鏈接更多精彩內(nèi)容