本文参与了1024 程序员节活动,欢迎正在阅读的你也加入。
经过多番查阅资料,发现只通过前端是无法完美处理该问题的。主要原因在于:
- js没有相关接口可以准确获取安卓手机软键盘的高度,这样也就无法处理当软键盘弹出时,能够将输入框顶上去,并且与软键盘完美的贴合,比如输入框在最底部的场景,此时页面滚动高度不够,因此无法准确的滚动到底,使得软键盘将输入框顶上去并与之完美的贴合。
- js并不能监听软键盘关闭的事件,比如点击折叠按钮关闭软键盘,此时输入框焦点还在,无法触发输入框的blur事件,有人说会触发resize事件,但是经过我的测试,resize事件并未触发。
以下是我的解决方案。首先封装一个获取页面视图宽高的工具函数,代码如下:
const getViewSize = ():{ width:number;height:number } {
if (window.innerWidth) {
return {
width: window.innerWidth,
height: window.innerHeight
};
} else if (document.compatMode === 'CSS1Compat') {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
} else {
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
}
}
其次,监听页面resize事件,同时也监听输入框的focus和blur事件。如下:
input.addEventListener('focus',this.onSetScrollHandler.bind(this,true));
input.addEventListener('blur',this.onSetScrollHandler.bind(this,false));
window.addEventListener('resize',this.onSetScrollHandler.bind(this,false));
接下来是onSetScrollHandler函数的处理,首先我们要考虑2种情况,第一那就是如果页面滚动高度足够,滚动到输入框时,页面的剩余滚动高度刚好可以大于等于软键盘的高度,此时就可以做到完美贴合输入框。那如果滚动高度不够,我们是需要将页面根元素高度给增大的,而我们是通过设置style的height来将根元素高度增大的。因此在触发blur事件或者是触发resize事件,高度变动时,就需要将根元素的高度恢复原样,因此我们需要先缓存页面的高度,以及是否存在高度的设置。如下:
const originHeight = getViewSize().height;
const originBodyHeight = document.body.style.height;
我们可以看到onSetScrollHandler是添加了一个布尔值参数的,用来做判断的,当然此时因为会触发resize事件,我们还需要单独计算状态。
此外由于这个问题是安卓手机出的,为了避免我们加的代码影响到ios手机又或者其它设备默认是实现的软键盘弹起顶上去的功能,我们需要添加环境的判断。如下:
type envReturnType = {
isBrowser: boolean;
isServer: boolean;
isMobile: boolean;
isAndriod: boolean;
isIos: boolean;
canUseWorkers: boolean;
canUseEventListeners: boolean;
canUseViewport: boolean;
}
const getEnv = (): envReturnType => {
const inBrowser = Boolean(typeof window !== 'undefined' && window.document && window.document.createElement);
const isMobileVailable = (reg: string | RegExp): boolean => Boolean(navigator.userAgent.match(reg));
const getEnvObject = [
isBrowser: inBrowser
isMobile: isMobileVailable(/(iPhoneliPod]Androidlios)/i)Test Regex...
isAndriod: isMobileVailable(/(android)/i),
isIos: isMobileVailable(/(iPhoneliPodlios)/i),
isServer: !inBrowser,
canUseWorkers: typeof Worker !== undefined!
canUseEventListeners: inBrowser && Boolean(window.addEventListener)
canUseViewport: inBrowser && Boolean(window.screen)
];
return Object.assign(0bject.values(getEnvObject),getEnvObject);
}
因此在onSetScrollHandler函数内部首先要做的就是判断是否是安卓手机。如下:
const onSetScrollHandler = (status: boolean) => {
const { isAndriod } = getEnv();
if(!isAndriod){
return;
}
// 后续代码
}
接着我们获取body元素的scrollTop,然后获取到需要被顶上去的输入框的scrollTop加上它的高度就是我们要滚动的距离,然后为了保证兼容性,我们需要设置document.body.scrollTop和document.documentElement.scrollTop,当然在部分安卓手机上,可能这2个设置都不会生效,这时候需要调用元素的scrollIntoView方法。同时我们还需要修改body元素的高度,由于无法获取到软键盘的准确高度,这时候我们需要修改body元素高度为2个屏幕高度,这样才能达到让剩余滚动高度足够大于软键盘高度。最终版本代码如下:
const onSetScrollHandler = (status: boolean) => {
const { isAndriod } = getEnv();
if(!isAndriod){
return;
}
const { height: resizeHeight } = getViewSize().height;
// 这里主要是为了还原
const { scrollTop } = document.body || document.documentElement;
if(status || resizeHeight < originHeight){
// 撑大根元素高度,方便滚动
document.body.style.height = `${originHeight + resizeHeight}px`;
const top = input.offsetHeight + input.scrollTop;
document.body.scrollTop = document.documentElement.scrollTop = top;
// 确保兼容性,还得调用scrollIntoView方法还原
input.scrollIntoView();
}else {
// 如果不存在原始高度,则移除height属性,否则重新设置height
if(!originBodyHeight){
document.body.style.removeProperty('height');
}else{
document.body.style.height = `${originBodyHeight}px`;
}
// 还原scrollTop
document.body.scrollTop = document.documentElement.scrollTop = scrollTop;
// 确保兼容性,还得调用scrollIntoView方法还原
document.body.scrollIntoView();
}
}
一个小小的软键盘遮挡问题,竟然要写出这么多的兼容性代码,兼容真的好蛋疼。
以上方案还并不算完美的处理掉了这个问题,正如开头提到的2个问题限制,因此当出现开头提到的场景,以上的代码就不能解决,也可以算作是体验问题。最完美的方案还得是端上做配合才行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。