IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    有关安卓软键盘遮挡输入框的问题分析及处理方案

    夕水发表于 2023-10-26 22:26:45
    love 0
    本文参与了1024 程序员节活动,欢迎正在阅读的你也加入。

    经过多番查阅资料,发现只通过前端是无法完美处理该问题的。主要原因在于:

    1. js没有相关接口可以准确获取安卓手机软键盘的高度,这样也就无法处理当软键盘弹出时,能够将输入框顶上去,并且与软键盘完美的贴合,比如输入框在最底部的场景,此时页面滚动高度不够,因此无法准确的滚动到底,使得软键盘将输入框顶上去并与之完美的贴合。
    2. 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个问题限制,因此当出现开头提到的场景,以上的代码就不能解决,也可以算作是体验问题。最完美的方案还得是端上做配合才行。



沪ICP备19023445号-2号
友情链接