彻底解决软键盘弹起遮挡输入框问题

13,040 阅读4分钟

一、问题描述

前提:一个被输入框填满的移动端页面

操作:当光标定位在某一个文本框上时,会自动弹起软键盘,这时候如果不做任何处理,则软键盘有可能遮挡靠近底部的文本框。

二、尝试解决 

    先介绍两个API:

   如果想看结果,可以忽略 这部分!

  • scrollIntoView() :

注意:MDN上面说这个是实验中的功能,所以在使用的时候还需要谨慎。

作用:方法让当前的元素滚动到浏览器窗口的可视区域内。

参数类型有两种,第一个种是Boolean ,第二种是Object 类型,该类型的参数处于实验性阶段,暂且不说,有兴趣去MDN上看看

参数为true时:改元素的顶端将和其所在滚动区的可视区域的顶端对齐
参数为false时:元素的底端将和其所在滚动区的可视区域的底端对齐

特别注意文档的最下面有这么一句话:

取决于其它元素的布局情况,此元素可能不会完全滚动到顶端或底端。

(我就是被这句话坑了。。。)

  • scrollIntoViewIfNeeded()    

MDN上面说这个不是一个非标准方法,不建议用在生产环境

该特性是非标准的,请尽量不要在生产环境中使用它!

作用:

方法用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。 如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动。 此方法是标准的Element.scrollIntoView()方法的专有变体。

下面就是使用上面两个API来尝试解决,代码如下:

const u = navigator.userAgent;

if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1) {  
    //安卓手机     
    window.addEventListener('resize', function () {        
        // Document 对象的activeElement 属性返回文档中当前获得焦点的元素。        
        if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {            
            console.log('安卓触发', document.activeElement.tagName)            
            window.setTimeout(function() { 
               // document.activeElement.scrollIntoView(true);                
               // document.activeElement.scrollIntoView(false);                
               //document.activeElement.scrollIntoViewIfNeeded();            
            }, 300);        
        }    
     })
}

看完官网的解释,我半信半疑的挨个试了,结果都不如所愿,有时候会超过,有时候还是会被遮挡,如下图

尝试方式一:

scrollIntoView(true);没有滚动到可视区域内,但是他确实滚动到上面,只是没有考虑到fixed顶部的头部的元素,遂弃之。

图1:

尝试方式二:

scrollIntoView(false); 没有滚动到可视区域内,但是从效果上看确实滚动到底部了,只是没有考虑到fixed 底部的按钮的元素,遂弃之。

图2:

尝试方式三:

scrollIntoViewIfNeeded 这个API,时好时坏,第一次是好的,在当前页面操作完一条数据后,光标再次回到第一个文本框就又被软键盘遮挡了(这里没有图。。。),遂弃之。

三、另寻出路

既然提供的现有的API不能满足,那就自己想办法吧。。。这里先说一下现在页面的布局,如下:别问为什么,因为祖传代码,我也不知道为什么这么布局。。。)

这里的页面布局先说一下 头部,和底部按钮都是fixed 定位,中间部分是 position 定位,高度是100% 

.header {
    position: fixed;
    top: 0px;
    left: 0px;
}
.content {
    position: abslute;
    top: 45px; // header 的高度
    left: 0px;
}
.footer {
    position: fixed;
    bottom: 0px;
    left: 0px;
}
  • 需求分析:我们要做的很简单,其实就是需要,当光标定位在当前文本框的时候,让他滚动到顶部,并减去头部元素高度的位置即可。当失去光标时,在让他滚回到原来位置即可。
  • 解决思路:既然有了思路,那么我们可以给每个文本框添加一个 focusblur 事件 当 focus时候,滚动的高度 = 文本框距离顶部的高度 - 头部的高度 ,当 blur失去焦点的时候,再设置滚动的高度为 0就行了。

当然吧input 抽出来一个组件,就只需要加一给这个组件上加这两个事件就行了了。

代码如下:

// content 就是这个页面容器的className
function addInputTop(event, content) {    
    var input = event.srcElement
    // 当前文本框 距离顶部的高度 减去 45即header的高度,当然,这里也可以减去动态计算的高度
    var top = input.getBoundingClientRect().top - 45      
    var current = document.querySelector('.' + content)    
    // 滚动到指定位置
    current.scrollTo(0, top)
}
function removeInputTop(event, content) {    
    var current = document.querySelector('.' + content)    
    current.scrollTo(0, 0)
}

注:getBoundingClientRect() 
这个方法可以返回一个对象,这个对象包括当前dom 元素距离上、下、左、右的距离。getBoundingClientRect 也是一个实验中的功能,但是兼容性还不错

至此,搞定,并且兼容性较好!第一次发掘金 ,若有错误还望各位指正。