前言
H5 有时底部按钮是固定定位,当页面上面需要用到输入内容时,会弹起软键盘,然后把底部固定的按钮也弹起来了,我们希望弹起软键盘时,底部的固定定位按钮隐藏,这里就需要对移动端软键盘弹起收起进行监听。
一、解决方法
/**
* 监听H5软键盘弹起
* @param callback
* @param differenceRange
* @returns
*/
export const monitorSoftKeyboard: Function = (
callback: ({ isUp, isDown }: { isUp?: boolean; isDown?: boolean }) => void, // 监听软键盘弹起或降落的回调
differenceRange: number = 20 // 默认差值范围 20
) => {
const originalHeight =
document.documentElement.clientHeight || document.body.clientHeight;
const onResize = () => {
// 键盘弹起与隐藏都会引起窗口的高度发生变化,这是主要的监听依据;
const resizeHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// 兼容IOS系统的某些大屏手机【从未弹起的软键盘clientHeight】和【弹起再收起后的clientHeight】不完全相同,存在差值;
// 如果差值小于默认差值范围,即20,则认为软键盘已收起。
if (Math.abs(originalHeight - resizeHeight) <= differenceRange) {
// 软键盘收起
callback({ isUp: false, isDown: true });
} else {
// 软键盘弹起
callback({ isUp: true, isDown: false });
}
};
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
};
// ------------------------------page.tsx-------------------------------
const [showBottom, setShowBottom] = useState<boolean>(true);
useEffect(() => {
const remove = monitorSoftKeyboard(({ isDown }) => {
if (isDown) {
setShowBottom(true);
} else {
setShowBottom(false);
}
});
return () => {
remove();
};
}, []);
二、Hooks 实现形式
import { useEffect, useState } from 'react';
type CallbackParams = {
isDown?: boolean;
isUp?: boolean;
};
type Props = {
callback?: (params?: CallbackParams) => void; // 监听软键盘弹起或降落的回调
differenceRange?: number; // 差值范围,默认 20,兼容IOS可用
};
/**
* 监听H5软键盘弹起
* @param props
* @returns
*/
const useMonitorSoftKeyboard = (props?: Props) => {
const [isSoftKeyboardDown, setIsSoftKeyboardDown] = useState<boolean>(true);
useEffect(() => {
const originalHeight = document.documentElement.clientHeight || document.body.clientHeight;
const monitorSoftKeyboard = () => {
// 键盘弹起与隐藏都会引起窗口的高度发生变化,这是主要的监听依据;
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
// 默认差值范围 20
const DIFFERENCE_RANGE = props?.differenceRange ?? 20;
// 兼容IOS系统的某些大屏手机【从未弹起的软键盘clientHeight】和【弹起再收起后的clientHeight】不完全相同,存在差值;
// 如果差值小于默认差值范围,即20,则认为软键盘已收起。
if (Math.abs(originalHeight - resizeHeight) < DIFFERENCE_RANGE) {
// 软键盘收起
setIsSoftKeyboardDown(true);
props?.callback?.({ isDown: true, isUp: false });
} else {
// 软键盘弹起
setIsSoftKeyboardDown(false);
props?.callback?.({ isDown: false, isUp: true });
}
};
window.addEventListener('resize', monitorSoftKeyboard);
return () => {
window.removeEventListener('resize', monitorSoftKeyboard);
};
}, [props]);
return { isSoftKeyboardDown, isSoftKeyboardUp: !isSoftKeyboardDown } as {
isSoftKeyboardDown: boolean;
isSoftKeyboardUp: boolean;
};
};
export default useMonitorSoftKeyboard;
三、解释
- 在android中软键盘弹起或收起时,会改变window的高度,因此监听window的onresize事件;但是 ios 中软键盘的弹起收起并不触发 window.onresize 事件;
- 在 ios 中软键盘弹起时,仅会引起 scrollTop 值改变,但是我们可以通过输入框的获取焦点情况来做判断,但也只能在 ios 中采用这个方案,因为在 android 中存在主动收起键盘后,但输入框并没有失焦,而ios中键盘收起后就会失焦;
- 另外,focusin和focusout支持冒泡,对应focus和blur, 使用focusin和focusout的原因是focusin和focusout可以冒泡,focus和blur不会冒泡,这样就可以使用事件代理,处理多个输入框存在的情况。