The processing of H5 mobile phone keyboard pop up

Time:2021-4-15

This article first appeared in hzzly’s blog

The original text of H5

Foreword: the front-end time also started the tossing journey of H5 mobile terminal according to the needs of the project, and expanded two TOC mobile terminal projects on the basis of the current middle platform. The following is a summary of the compatibility of pop-up and stow of the keyboard on the H5 mobile terminal form page.

problem

In the H5 project, we often encounter some form pages. When we get the focus in the input box, we will automatically trigger the keyboard to pop up. However, the performance of keyboard pop-up in IOS and Android’s WebView is not the same. At the same time, when we actively trigger the keyboard to collapse, there are also differences.

Keyboard pop up

  • IOS: the keyboard of IOS system is at the top of the window. When the keyboard pops up, the height of WebView does not change, but the scrolltop changes, and the page can scroll. And the maximum page scrolling is the height of the pop-up keyboard. Only when the keyboard pops up and the page just scrolls to the bottom, the change value of scrolltop is the height of the keyboard, which cannot be obtained in other cases. This makes it difficult to get the real height of the keyboard in IOS.
  • Android: in the Android system, the keyboard is also at the top of the window. When the keyboard pops up, if the input box is near the bottom, it will be blocked by the keyboard. Only when you enter, the input box will scroll to the visualization area.

Keyboard up

  • IOS: when the button on the keyboard is triggered to retract the keyboard or the page area outside the input box, the input box will lose focus, so the blur event of the input box will be triggered; when the keyboard is retracted, a blank area will appear at the bottom of the page, and the page will be lifted.
  • Android: when the button on the keyboard is triggered and the keyboard is folded, the input box will not lose focus, so the blur event of the page will not be triggered; when the area outside the input box is triggered, the input box will lose focus, and the blur event of the input box will be triggered.

Expected results

In view of the differences in the pop-up and pop-up of keyboards triggered by different systems, we hope to maintain the consistency of user experience as well as smooth functions.

an antidote against the disease

Above, we have sorted out the differences between the two major systems on the market, and then we need to suit the remedy to the case.

In H5, there is no interface to directly monitor keyboard events, but we can judge whether the keyboard is ejected or stowed by analyzing the triggering process and manifestation of keyboard ejecting and stowing.

  • Keyboard pop-up: when the input box gets the focus, it will automatically trigger the keyboard pop-up action. Therefore, we can monitor the focus in event to realize the page logic required after the keyboard pop-up.
  • Keyboard stowing: when other page areas are triggered to stow the keyboard, we can monitor the focus out event to implement the page logic required after the keyboard is stowed. There are differences between IOS and Android when the keyboard is folded by keyboard button

    • IOS: if the focus out event is triggered, it is still monitored by this method.
    • Android: no focus out event was triggered. In Android, keyboard state switching (pop-up, stow) is not only associated with the input box, but also affects the change of WebView height, so we can monitor the change of WebView height to determine whether the keyboard is stowed.

System judgment

In practice, we can judge the current system through the user agent

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

IOS processing

Let isreset = true; // return

this.focusinHandler = () => {
  Isreset = false; // when focusing, the keyboard pops up. When the focus is switched between input boxes, the defocus event of the previous input box will be triggered first, and then the focus event of the next input box will be triggered
};

this.focusoutHandler = () => {
  isReset = true;
  setTimeout(() => {
    //When the focus switches between the input boxes of the pop-up layer, it does not return to the original position
    if (isReset) {
        window.scroll (0, 0); // confirm that the next element is not focused after the delay, and the defocusing is caused by closing the keyboard. Force the page to return
    }
  }, 30);
};

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

Android processing

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) {
    //Logic after keyboard pop up
    if (activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA")) {
      setTimeout(()=>{
        activeElement.scrollIntoView ({block: 'center'}); // the problem of rolling the focus element to the visible area
      },0)
    }
  } else {
    //Logic after keyboard retraction
  }
};

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

React encapsulation

In react, we can write a class decorator to decorate the form component.

Class decorator: the class decorator is declared before the class declaration (next to the class declaration). Class decorators are applied to class constructors and can be used to monitor, modify, or replace class definitions.

// keyboard.tsx
/*
 *@ Description: keyboard processing decorator
 * @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 processing above
        ...
      }
      if (isAndroid) {
        //It's handled by 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;

use

// PersonForm.tsx
@keyboard()
class PersonForm extends PureComponent<{}, {}> {
  //Business logic
  ...
}

export default PersonForm;