UNPKG

virtual-keyboard-offset

Version:

A react hook that calculates the distance that your components are offset and the size of the remaining viewport when iOS/Android mobile virtual keyboards open

90 lines (79 loc) 3.17 kB
import { useState, useLayoutEffect, useEffect } from "react"; import { useCurrentTime, getBrowser, getDeviceType } from "./utils"; const useKeyboardOffset = () => { const [keyBoardOffset, setKeyBoardOffset] = useState(0); const container = document.getElementById("keyboardOffsetAnchor") || {}; const body = document.body || {}; const childNodes = body.childNodes || []; const browser = getBrowser(); const isMobile = getDeviceType(); //see Safari note below const needsBackup = isMobile && browser === "Apple Safari" && keyBoardOffset < 0; //i dont want to run this function very often and overload frontends when its not needed const delay = needsBackup ? 250 : 250000; const seconds = useCurrentTime(delay); const backupCheck = needsBackup ? seconds : 0; //Safari on iOS creates the most virtual keyboard issues, it doesnt throw any event when the user closes the keyboard //with the return or go buttons. Current solution is simply to keep checking if its been closed any time its open. //Happy to hear better work arounds here. useEffect(() => { if (container.id) { const bReq = container.getBoundingClientRect() || {}; if (keyBoardOffset < 0 && bReq !== keyBoardOffset) { setKeyBoardOffset(bReq.top); } } }, [backupCheck]); //the anchor is the crux of this project, we insert the sizeless div and track its position against the viewport const insertAnchor = () => { let meas = document.createElement("div"); meas.id = `keyboardOffsetAnchor`; meas.style.width = "0px"; meas.style.height = "0px"; meas.style.position = "absolute"; meas.style.left = "0px"; meas.style.top = "0px"; body.insertBefore(meas, body.firstChild); }; //insert the anchor whenever we dont have one but only on mobile (functionality is irrelavant on desktop browsers) useEffect(() => { if (!container && isMobile) { insertAnchor(); } }, []); //listen for various events that may indicate that the keyboard has opened again only on mobile useLayoutEffect(() => { if (isMobile) { window.addEventListener("click", delayEvent); window.addEventListener("blur", delayEvent); window.addEventListener("focus", delayEvent); window.addEventListener("keyup", delayEvent); delayEvent(); } return () => { if (isMobile) { window.removeEventListener("click", delayEvent); window.removeEventListener("blur", delayEvent); window.removeEventListener("focus", delayEvent); window.removeEventListener("keyup", delayEvent); } }; }, []); //some of the virtual keyboards move slightly slower than others, the below //delay makes sure the elements have moved to their final position when we save it const delayEvent = () => { setTimeout(() => handleEvent(), 100); }; const handleEvent = () => { if (container.id) { const bReq = container.getBoundingClientRect() || {}; setKeyBoardOffset(bReq.top); } }; return { keyBoardOffset: keyBoardOffset, windowHeight: window.innerHeight + keyBoardOffset }; }; export { useKeyboardOffset };