精华内容
下载资源
问答
  • h5底部输入框被键盘遮挡问题
    2021-04-10 17:23:23

    在h5页面中,当输入框在最底部,点击软键盘会出现输入框会被遮挡问题,或者是底部有一个固定定位的导航,然而每次一点击输入框,弹出输入法的时候,这个底部的导航会随着输入法被顶上去问题。

    解决办法:由于弹起输入法,会执行onresize 事件,根据窗口变化,将原先是固定定位的元素改为position:static;。当关闭输入法时再切换回position:absolute;。

    var getHeight = $(document).height();
    
    $(window).resize(function(){
     if($(document).height() < getHeight) {
      $('#footer').css('position','static');
     }else {
      $('#footer').css('position','absolute');
     }
    });
    
    更多相关内容
  • 1、ios端兼容input光标高度问题详情描述:input输入框光标,在安卓手机上显示没有问题,但是在苹果手机上当点击输入的时候,光标的高度和父盒子的高度一样。例如下图,左图是正常所期待的输入框光标,右边是ios的...

    最近给公司微信公众号,写了微信h5业务页面,总结分享一下前端开发过程中的几个兼容性坑,项目直接拿的公司页面,所以下文涉及图片都模糊处理了。

    1、ios端兼容input光标高度

    问题详情描述:input输入框光标,在安卓手机上显示没有问题,但是在苹果手机上

    当点击输入的时候,光标的高度和父盒子的高度一样。例如下图,左图是正常所期待的输入框光标,右边是ios的input光标。

    e4b16e8afa2cc86dac0f6bd6833b3484.png

    出现原因分析:通常我们习惯用height属性设置行间的高度和line-height属性设置行间的距离(行高),当点击输入的时候,光标的高度就自动和父盒子的高度一样了。(谷歌浏览器的设计原则,还有一种可能就是当没有内容的时候光标的高度等于input的line-height的值,当有内容时,光标从input的顶端到文字的底部

    解决办法:高度height和行高line-height内容用padding撑开

    例如:

    .content{  float: left;  box-sizing: border-box;  height: 88px;  width: calc(100% - 240px);                .content-input{     display: block;     box-sizing: border-box;     width: 100%;     color: #333333;     font-size: 28px;     //line-height: 88px;     padding-top: 20px;     padding-bottom: 20px;  }}

    2、ios端微信h5页面上下滑动时卡顿、页面缺失

    问题详情描述:在ios端,上下滑动页面时,如果页面高度超出了一屏,就会出现明显的卡顿,页面有部分内容显示不全的情况,例如下图,右图是正常页面,边是ios上下滑动后,卡顿导致如左图下面部分丢失。

    4c062ef4938cc503b080b58d411e1f50.png

    出现原因分析:

    笼统说微信浏览器的内核,Android上面是使用自带的WebKit内核,iOS里面由于苹果的原因,使用了自带的Safari内核,Safari对于overflow-scrolling用了原生控件来实现。对于有-webkit-overflow-scrolling的网页,会创建一个UIScrollView,提供子layer给渲染模块使用。【有待考证】

    解决办法:只需要在公共样式加入下面这行代码

    *{  -webkit-overflow-scrolling: touch;}

    But,这个属性是有bug的,比如如果你的页面中有设置了绝对定位的节点,那么该节点的显示会错乱,当然还有会有其他的一些bug。

    拓展知识: -webkit-overflow-scrolling:touch是什么?

    MDN上是这样定义的:

    -webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果. auto : 使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。 touch : 使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会创建一个新的堆栈上下文。

    3、ios键盘唤起,键盘收起以后页面不归位

    问题详情描述:

     输入内容,软键盘弹出,页面内容整体上移,但是键盘收起,页面内容不下滑

    出现原因分析:

    固定定位的元素 在元素内 input 框聚焦的时候 弹出的软键盘占位 失去焦点的时候软键盘消失 但是还是占位的 导致input框不能再次输入 在失去焦点的时候给一个事件

    解决办法:

    class="list-warp"> <div class="title"><span>投·被保险人姓名span>div>
    class="content"> <input class="content-input" placeholder="请输入姓名" v-model="peopleList.name" @focus="changefocus()" @blur.prevent="changeBlur()"/>    div> </div>
    changeBlur(){   let u = navigator.userAgent, app = navigator.appVersion;   let isIOS = !!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/);   if(isIOS){      setTimeout(() => {         const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0         window.scrollTo(0, Math.max(scrollHeight - 1, 0))      }, 200)   } }

    拓展知识: position: fixed的元素在ios里,收起键盘的时候会被顶上去,特别是第三方键盘

    4、安卓弹出的键盘遮盖文本框

    问题详情描述:

    安卓微信H5弹出软键盘后挡住input输入框,如下左图是期待唤起键盘的时候样子,右边是实际唤起键盘的样子

    1959f06738a17c5a2fa9fa45868ef81a.png

    出现原因分析:待补充

    解决办法:给input和textarea标签添加focus事件,如下,先判断是不是安卓手机下的操作,当然,可以不用判断机型,Document 对象属性和方法,setTimeout延时0.5秒,因为调用安卓键盘有一点迟钝,导致如果不延时处理的话,滚动就失效了

    changefocus(){   let u = navigator.userAgent, app = navigator.appVersion;   let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;   if(isAndroid){      setTimeout(function() {      document.activeElement.scrollIntoViewIfNeeded();      document.activeElement.scrollIntoView();      }, 500);   }}

    拓展知识:

    Element.scrollIntoView()方法让当前的元素滚动到浏览器窗口的可视区域内。而Element.scrollIntoViewIfNeeded()方法也是用来将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域。但如果该元素已经在浏览器窗口的可见区域内,则不会发生滚动

    5、Vue中路由使用hash模式,开发微信H5页面分享时在安卓上设置分享成功,但是ios的分享异常

    问题详情描述:

    ios当前页面分享给好友,点击进来是正常,如果二次分享,则跳转到首页;使用vue router跳转到第二个页面后在分享时,分享设置失败;以上安卓分享都是正常

    c6a15eba3e73f841917152f5a8b07ba6.png

    出现原因分析:jssdk是后端进行签署,前端校验,但是有时跨域,ios是分享以后会自动带上 from=singlemessage&isappinstalled=0 以及其他参数,分享朋友圈参数还不一样,貌似系统不一样参数也不一样,但是每次获取url并不能获取后面这些参数

    解决办法:

    (1)可以使用改页面this.$router.push跳转,为window.location.href去跳转,而不使用路由跳转,这样可以使地址栏的地址与当前页的地址一样,可以分享成功(适合分享的页面不多的情况下,作为一个单单页运用,这样刷新页面跳转,还是..)

    (2)把入口地址保存在本地,等需要获取签名的时候 取出来,注意:sessionStorage.setItem(‘href’,href); 只在刚进入单应用的时候保存!【该方法未验证】

    题外话:

    如果能用小程序写的页面,尽量上小程序吧,H5开发在微信开发者工具里看页面效果可能看不出问题,因为不能唤起软键盘。避免频繁线上发布,可以用花生壳或者idcfengye,做内网穿透,搭建一个可以通过域名访问的开发环境的h5页面,在手机上看看效果,对了微信内置浏览器缓存机制。会导致刚提交的代码(特别是js)效果要半个小时左右才生效。

    最后:

    微信H5页面其实很多知识,登陆授权,jssdk授权,这里就只做了分享,当然还有上传图片、微信支付等功能,都可能会遇到坑,以上几个坑也是比较常遇到的,如果有更好的解决方案的话,欢迎在留言区分享

    作者:codercao

    https://juejin.im/post/5d47d2eff265da03f77e4e3a

    fa7209abf27a847cd63ecb5cb623e793.png

    55c3d37b14e440f5b176e563281b0f50.png点这,与大家一起分享本文吧~
    展开全文
  • ”在 H5 项目中,我们会经常遇到页面中存在单个甚至多个 input/textarea 输入框底部固定元素的布局情况。在 input/textarea 输入框获取焦点时,会自动触发键盘弹起,而键盘弹出在 ios 与 android 的 webview 中...

    019c380f8967a84d294eeceeb1c93507.png

     我们经常需要填写表单,键盘的行为存在各种差异。

    在 H5 项目中,我们会经常遇到页面中存在单个甚至多个 input/textarea 输入框与底部固定元素的布局情况。在 input/textarea 输入框获取焦点时,会自动触发键盘弹起,而键盘弹出在 ios 与 android 的 webview 中表现并非一致,同时当我们主动触发键盘收起时也同样存在差异化。而无论如何,我们希望功能流畅的同时,尽量保持用户体验的一致性,因此有了下面一系列兼容性问题的研究。

    01

    键盘弹出的不同表现

    • IOS:IOS 的键盘处在窗口的最上层,当键盘弹起时,webview 的高度 height 并没有改变,只是 scrollTop 发生变化,页面可以滚动。且页面可以滚动的最大限度为弹出的键盘的高度,而只有键盘弹出时页面恰好也滚动到最底部时,scrollTop 的变化值为键盘的高度,其他情况下则无法获取。这就导致在 IOS 情况下难以获取键盘的真实高度。

    • Android: webview 中留出空间,该空间小于等于的键盘空间,变化的高度差会随着布局而不同,有的认为 键盘高度 + 页面高度 = 原页面高度; 是错误的误导,只有在某种很巧合的布局情况下才可套用此公式。

    02

    键盘收起的不同表现

    • IOS:触发键盘上的按钮收起键盘或者输入框以外的页面区域时,输入框会失去焦点,因此会触发输入框的 blur 事件。

    • Android: 触发键盘上的按钮收起键盘时,输入框并不会失去焦点,因此不会触发页面的 blur 事件;触发输入框以外的区域时,输入框会失去焦点,触发输入框的 blur 事件。

    03

    监听键盘的弹出与收起

    在 h5 中目前没有接口可以直接监听键盘事件,但我们可以通过分析键盘弹出、收起的触发过程及表现形式,来判断键盘是弹出还是收起的状态。

    • 键盘弹出:输入框获取焦点时会自动触发键盘的弹起动作,因此,我们可以监听输入框的 focus 事件,在里面实现键盘弹出后所需的页面逻辑。这在 ios 及 android 中表现一致。

    • 键盘收起:从第 2 部分可知,触发键盘收起的不同形式会存在差异化表现,当触发其他页面区域收起键盘时,我们可以监听输入框的 blur 事件,在里面实现键盘收起后所需的页面逻辑。而在通过键盘按钮收起键盘时在 ios 与 android 端存在差异化表现,下面具体分析:

    • IOS:触发了输入框 blur 事件,仍然通过该办法监听。

    • Android:没有触发输入框的 blur 事件。但通过第 1、2 部分我们可以知道,在 android 中,键盘的状态切换(弹出、收起)不仅和输入框关联,同时还会影响到 webview 高度的变化,那我们不妨通过监听 webview height 的变化来判断键盘是否收起。

    下面举例说明,其中页面中含有一个输入框:

    class="txd">

       Welcome to TXD!  

    class="input">

        id="input" type="tel" />

    ios & android 键盘弹出:

    const $input = document.getElementById('input');

    $input.addEventListener('focus', () => {

       // 处理键盘弹出后所需的页面逻辑

    }, false);

    ios 键盘收起:

    const $input = document.getElementById('input');

    $input.addEventListener('blur', () => {

       // 处理键盘收起后所需的页面逻辑

    }, false);

    android 键盘弹出与收起:

    /*键盘弹起后页面高度变小*/

    const originHeight = document.documentElement.clientHeight || document.body.clientHeight;

    window.addEventListener('resize', () => {

       const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;

       if (resizeHeight < originHeight) {

           // 键盘弹起所后所需的页面逻辑

       } else {

           // 键盘弹起所后所需的页面逻辑

       }

    }, false);

    在实践中通过判断 userAgent 来决定使用哪种方法:

    const ua = window.navigator.userAgent.toLocaleLowerCase();

    const isIOS = /iphone|ipad|ipod/.test(ua);

    const isAndroid = /android/.test(ua);

    04

    使用场景

    接下来根据上面的讨论说明几种常见场景:

    (1)头部及中间输入框处于正常的文档流,底部元素 fixeda22db2432e3546f74df123b3e5738f13.png

    ios 键盘遮挡在页面上,页面高度始终不变,页面可以滚动,底部元素被遮挡;

    android 页面高度减少,页面不可滚动,fixed 元素的 bottom 属性的基线为键盘;

    (2)头部及输入框处于正常文档流,且所占可视区域变大,底部元素 fixed

    090305f8b6cc23627e5429cc14026c4c.png

    ios 的 height 没有发生变化,页面可以滚动;

    android 页面高度变小,但为了使正常文档流的元素可以正常显示,页面可以上下滚动,fixed 元素的 bottom 属性的基线为键盘;

    (3)头部处于正常文档流,输入框脱离正常文档流 fixed bottom 定位5caec11afa3346e33b121b6a1422be8c.png
    (上面为了便于观察,将输入框包裹元素的背景设置了透明度)

    ios 的 height 没有发生变化,且始终保证输入框处于可视区域中;

    android 页面高度变小,页面不可滚动,fixed 输入框 bottom 属性的基线为键盘;

    05

    小结

    1. 在 ios 中,无论何种布局,为了使输入框展示在可视区域中,键盘弹出时,页面会向上滚动,该过程与 Element.scrollIntoViewIfNeeded() 方法(将不在浏览器窗口的可见区域内的元素滚动到浏览器窗口的可见区域)产生的效果一致;且高度始终不变,页面可滚动。

    2. 在 android 中,键盘唤起后,页面可滚动与否由其处在正常文档流中的元素决定:如果正常文档流中的元素可全量展示,页面不可滚动,否则页面支持滚动;

    3. 在 android 中,键盘唤起后,fixed 元素的基准会发生变化:根据 bottom 定位的元素,其基线变为键盘上部;根据 top 定位的元素,仍然根据页面顶部,因此为照顾正常文档流及 fixed 元素的用户体验,有的元素可根据顶部定位,有的可以根据底部定位。

    在了解清楚 h5 中键盘的弹出收起的性质后,在处理兼容性问题会容易很多。同时也可使用Element.scrollIntoViewIfNeeded() 方法辅助解决问题(比如在切换不同的输入法时,可能导致有用信息被遮挡的情况)优化体验。

    c3e75a76c95710e4b63a5da239339580.png

    展开全文
  • 今天我要来说说键盘遮挡输入框问题, 并和大家分享我的解决方案.首先, 需要区分iOS和Android.iOS: 我司用的是UIWebview, 经过验证, >=iOS8.0的版本都会在键盘弹起时自动调整输入框的位置, 让它始终位于可视区域...

    17fb9ca6-0214-eb11-8da9-e4434bdf6706.png

    当前app往往是native + h5的混合开发模式, 那么原生的体验不用说, 自然是极好的, 而h5总是有各种各样的兼容性问题以及一些体验问题. 今天我要来说说键盘遮挡输入框的问题, 并和大家分享我的解决方案.

    首先, 需要区分iOS和Android.

    1. iOS: 我司用的是UIWebview, 经过验证, >=iOS8.0的版本都会在键盘弹起时自动调整输入框的位置, 让它始终位于可视区域内, 所以在iOS中一路绿灯.
    2. Android: 我用安卓手机看了很多h5页面的表现, 其中包括滴滴、支付宝等大型公司的一些表单页面, 当input聚焦, 键盘弹起后, 有时webview会自动调整input使其处于可视区域内; 但有时却不会调整input位置, 这样键盘就把input给遮住了, 用户想要看到自己输入的文字, 那就必须把页面往上拖动. 至此, 输入框被遮挡而引发的用户体验问题就凸显出来了.

    那么, 该怎么解决呢? 我有以下几个思路:

    首先我想到的肯定是scrollIntoViewIfNeeded, 以及网上的很多解决方案也是这个. 可是这个api还是别指望了, hybrid app上根本不靠谱, 大把安卓的webview都不兼容.

    接下来我要开始表演了

    1.不解决

    把滴滴的app拿给交互看, '你看, 人家滴滴的h5也是这样子, 这个就是安卓浏览器的一个bug啊, 没法解决' --.--(噗嗤)

    18fb9ca6-0214-eb11-8da9-e4434bdf6706.png
    不好意思, 我被啪啪打脸了

    2.让原生处理, 当键盘弹起时, 把webview往上移动键盘的高度

    那么当键盘弹起的时候, app是怎么做到原生控件不被键盘遮挡的呢? 其实很简单, 原生可以监听键盘弹起的事件, 并获取键盘的高度, 将页面往上移动与键盘等高的距离.

    于是, 我拍起桌子, 冲着客户端组的同事说: "你们app就该在键盘弹起的时候, 把webview往上移啊!"

    19fb9ca6-0214-eb11-8da9-e4434bdf6706.png
    不好意思, 我又被打了, 出血量进一步加大

    说白了, 其实我们写的h5页面和native并无瓜葛, 只是因为此时在同一app内, 所以让native监听键盘弹起事件移动webview听起来还算靠谱, 但假如现在用安卓手机浏览器打开页面, 那此时键盘遮挡input的问题还是存在.

    ****************************************求人不如求己****************************************

    解决思路: 只需要在键盘弹起的时候, 判断当前focus的input是否在可视区域内, 如果不在可视区域内, 通过某种途径算出键盘的高度, 然后将input往上移动键盘高度的距离即可.

    那么该怎么计算出键盘高度呢?

    1bfb9ca6-0214-eb11-8da9-e4434bdf6706.png
    以下不靠谱的思路又来了

    3.在页面添加一个fixed button, 当键盘弹起时, 该button会随着键盘往上移动, 这样就能算出键盘高度了

    这时候, 我打算靠自己h5来实现整个功能了, 别人都是靠不住的.

    然后我就想封装一个Input组件, 大概是这个样子:

    <>
      

    通过这个组件, 每当键盘弹起的时候, 底部fixed的div, 会紧贴着键盘往上移动, 这样子就可以通过键盘弹起前div.getBoundClientRect().top - 键盘弹起后div.getBoundClientRect().top, 算出键盘的高度.

    可是仔细一想, 这样虽然能获取到键盘高度, 看着似乎也能解决键盘遮挡的问题, 可是要应用到项目中, 那岂不是要将项目里所有的<input />替换成上面封装的<Input />, 而且不仅仅是这样, <Input />所在之处, 必然添加一个无用的fixed button. 另外, 再往远了想, 如果要应用到其他项目, 难道要所有同事, 所有项目都这么干吗?

    显然...我还会被打~

    这方案不行, 我下一步该怎么走了呢? 我到底该怎么获取到键盘高度呢?

    这时候, 我想到了jsbridge(偷笑).

    4.实现一个获取键盘高度的jsbridge

    1cfb9ca6-0214-eb11-8da9-e4434bdf6706.png
    安卓大佬, 我又回来了, &amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;求人不如求己, 这话我没说过~~ 所以你能帮我实现一个获取键盘高度的jsbridge吗? 我知道这个方案不通用, 可是咱们好歹先搞个保底方案啊!&amp;amp;amp;amp;amp;amp;amp;amp;amp;#39;

    过了几分钟, 安卓大佬就按照我的意愿给我写了个api, 舒服~~. 于是代码是这样的:

    handleFocus = () => {
      bridge
        .getKeyboardHeight()
        .then(res => {
          window.alert(res.value);
      });
    }
    <input onFocus={this.handleFocus} />

    大佬还很善良的将获取到的键盘高度根据dpr(device pixel ratio设备像素比), 转换成我们需要的css像素. 哈哈, 完美, 准备下班~

    等等, 这有些安卓机卡, 在执行handleFocus的时候键盘还没弹起(部分机型有延迟), 所以此时app还没获取到键盘高度, 返回了0; "嗨, 好说好说, 我在加个setTimeout(, 200)". 搞定收工~

    1efb9ca6-0214-eb11-8da9-e4434bdf6706.png

    可是, 再想想, 真的搞定了吗, 这种方案, 自己作为一名有追求的开发, 这么丑陋且不通用的解决方案你会接受吗? 答案显然是无法接受的...

    因为我们不仅仅要依靠native, 而且所有input出现的地方都要实现onFocus事件. 这显然是不合理的, 因为真的不够通用, 那么我们到底有没有一个方案, 只需要写一次即可处理项目里的所有input呢?

    -----------------------------------------分割线-------------------------------------------

    以下将会是正经的解决方案思路:

    1.首先, 曾经在网上看到过这么一段代码, 这里给了我一部分思路:

    window.addEventListener('resize', () => {     
        if (document.activeElement.tagName == 'INPUT') {         
            //延迟出现是因为有些 Android 手机键盘出现的比较慢
             window.setTimeout(() => {             
                document.activeElement.scrollIntoViewIfNeeded();
             }, 100);
         }
    });
    /* 当然这段代码是行不通的, 因为scrollIntoViewIfNeeded的兼容性问题 */

    在这里我们能提取出一个思路, 键盘弹起会触发window.onresize事件, 那么刚才那个疑惑(什么时候去获取键盘高度)就解决了.

    而且, 在这段代码中我们还有另外一个收获, 我们可以通过document.activeElement获取当前聚焦的元素. 所以这时候我想到了下面这个方案, 暂时用伪代码表示:

    window.addEventListener('resize', () => {     
        if (document.activeElement.tagName == 'INPUT') {         
            包裹document.activeElement的可滚动元素向上滚动键盘的高度
         }
    });

    2. 怎么获取键盘高度呢, 曾经我在网上看到过另外一段代码, 博主号称只要添加以下这行代码, 即可解决键盘遮挡输入框的问题(大家不用试了, 显然这肯定是不可能的~~).

    document.body.height = window.screen.availHeight;

    但是这段代码, 给了我另一个思路. "是啊, 既然当键盘弹起的时候会触发window.onresize事件, 那么视口的高度应当会减小, body的高度也应当会减小."

    于是我就开始尝试了起来. 首先

    window.onresize = () => {
      window.alert(window.screen.availHeight);
    }

    很遗憾, 经过调试发现, window.screen.availHeight的值并没有发生改变;

    那么document.body.clientHeight或者offsetHeight呢?

    window.onresize = () => {
      window.alert(document.body.clientHeight);
    }

    哇, 发现新大陆了, 思路从此打通, 经过调试, document.body.clientHeight的值发生了改变~~

    1ffb9ca6-0214-eb11-8da9-e4434bdf6706.png

    到这, 其实整体思路已经打通, 但是还有一个小点, 就是我们真的需要把input往上移动与键盘等高的距离吗? 其实我心里有点小担忧, 我怕会出现这种情况: input往上移动键盘的高度后, 会超出视口的顶部可是区域(也就是移过头了)--这个我不是很确定, 仅仅只是一种担忧, 所有我采取了稳妥的解决方案, 将input移动至视口可视区域的底部. 那么此时要移动多少距离呢? 我画了一幅图:

    20fb9ca6-0214-eb11-8da9-e4434bdf6706.png

    我们先来看看左图, 通过prev = input.getElementBoundingClientRect().bottom可以获取input底部距离视口顶部的距离; 然后弹起键盘后, 我们将input移动到页面可视区域的底部, 此时cur = input.getElementBoundingClientRect().bottom === document.body.clientHeight; 所以我们可以通过prev - cur得到input需要移动的距离.

    说道移动input, 要怎么移呢? 分为2种情况, 一种是流式布局, 此时我们只需要调用window.scrollTo(x, y); 第二种情况就是我们给html, body, div#root都加了height: 100%样式, 然后给每个页面的包裹元素添加{height: 100%; overflow-y: auto;}样式. 这样就需要设置pageContainer.scrollTop -= input需要移动的距离;

    至此, 实现通用方案的思路已经理清了, 接下来让我们来看看代码:

    首先, 我们需要使用到getBoundingClientRect这个api, 让我们来封装一个通用的函数;

    function getBoundingClientRect(element) {
      if (!element) {
        return null;
      }
      if (!element.getClientRects().length) {
        return null;
      }
    
      return element.getBoundingClientRect();
    }

    其次, 需要一个通用函数来获取元素目前在垂直方向上已滚动的距离, 该函数需要区分window和普通元素;

    // 获取window的偏移量, 或者元素的scroll偏移量
    function getScroll(target = window, isTop = true) {
      if (typeof window === 'undefined') {
        return 0;
      }
    
      const prop = isTop ? 'pageYOffset' : 'pageXOffset';
      const method = isTop ? 'scrollTop' : 'scrollLeft';
      const isWindow = target === window;
    
      const ret = isWindow ? target[prop] : target[method];
    
      return ret;
    }

    再接着我们需要封装一个滚动元素的通用函数, 也需要区分window和普通元素;

    function scrollTo(target, originScrollTop, targetScrollTop) {
      if (!target) return;
      if (!isNumber(originScrollTop) || !isNumber(targetScrollTop)) return;
    
      const reqAnimFrame = getRequestAnimationFrame();
    
        let start = null;
        // 该函数的参数: 现在距离最开始触发requestAnimationFrame callback的时间间隔, 但是它的值不为0
        const frameFunc = (timestamp) => {
          if (!start) {
            start = timestamp;
          }
          const realTimestamp = timestamp - start;// 当前时间
          const isWindow = target === window;
    
          if (isWindow) {
            window.scrollTo(
              window.pageXOffset,
              easeInOutCubic(realTimestamp, originScrollTop, targetScrollTop, 200),
            );
          } else {
            target.scrollTop = easeInOutCubic(realTimestamp, originScrollTop, targetScrollTop, 200);
          }
    
          if (realTimestamp < 200) {
            reqAnimFrame(frameFunc);
          }
        };
    
        reqAnimFrame(frameFunc);
    }

    写好滚动函数, 为了优化体验, 让滚动不显得过于突兀, 我加入了缓动函数以及帧动画

    /**
     *
     * @param {*} t timestamp: 当前时间 - 动画最开始执行的那一刻
     * @param {*} b 开始状态
     * @param {*} c 结束状态
     * @param {*} d duration: 期待动画持续的时间
     */
    function easeInOutCubic(t, b, c, d = 450) {
      const cc = c - b;
    
      t /= d / 2;
      if (t < 1) {
        return cc / 2 * t * t * t + b;
      }
    
      // eslint-disable-next-line
      return cc / 2 * ((t -= 2) * t * t + 2) + b;
    }
    
    function getRequestAnimationFrame() {
      return window.requestAnimationFrame
          || window.mozRequestAnimationFrame
          || window.webkitRequestAnimationFrame
          || window.msRequestAnimationFrame;
    }

    写好了上面的工具函数, 我们将其拼接起来

    function bubbleInputIfNeeded() {
      const target = _target;
      const offsetBottom = _offsetBottom;
    
      // 获取文档当前聚焦的元素
      const { activeElement } = document;
    
      if (!['INPUT', 'TEXTAREA'].includes(activeElement.tagName.toUpperCase())) return;
      const rect = getBoundingClientRect(activeElement);
    
      if (!rect) return;
    
      // 判断当前focus的input底部是否在document.body可视区域内
      if (rect.bottom > document.body.clientHeight - offsetBottom) {
        prevBodyHeight = document.body.clientHeight;
    
        // window或者父元素已经滚动的距离
        originScrollTop = getScroll(target);
    
        /** 
         * 元素需要滚动的距离
         * ps: 在安卓手机上, 键盘弹起的时候, document.body的高度会减小为可视区域的高度
        */
        const elementNeedScroll = rect.bottom - document.body.clientHeight + offsetBottom;
        const targetScrollTop = originScrollTop + elementNeedScroll;
    
        scrollTo(target, originScrollTop, targetScrollTop);
      }
    }

    最后, 我们需要监听window的resize事件

    let eventListener;
    function work() {
      if (typeof window === 'undefined') return;
    
      if (isAndroid()) {
        window.addEventListener('resize', eventListener = bubbleInputIfNeeded.bind(this));
      }
    }

    好了, 一个通用的方案到此基本完成了. 当然还需要有几个注意的点:

    1. 如果页面底部存在fixed布局的按钮, 岂不是要遮住输入框? 没事的, 我添加了一个机制, 可以让使用者设置 _offsetBottom(代表输入框距离可视区域底部的高度, 这种情况下你只需设置为按钮的高度即可)
    2. 如果页面的布局十分复杂怎么办, input处于层层嵌套的div下面. 这种情况下, 那真的很抱歉, 有些方案是需要对使用者做一些约束的, 它仅仅支持我在上文提到的2中基于流式布局的情况, 参考ant·design的Anchor组件, 也只能支持流式布局的页面, 要不然如果页面层层嵌套, 当然通过循环遍历依旧能解决, 但是这性能已经差到极致了.
    3. 第三步, 我该怎么引用呢? 然后如果我在不同页面需要滚动的目标元素不同, 又该怎么处理呢? 放心, 为了方便使用, 我导出了一个对象, 以及一些方法供大家使用.
    let _offsetBottom = 0;
    let _target = window;
    
    const BubbleInput = {
      setOffsetBottom(offsetBottom) {
        _offsetBottom = offsetBottom;
        return this;
      },
      setTarget(target) {
        _target = target;
        return this;
      },
      reset() {
        this.setOffsetBottom(0);
        this.setTarget(window);
        return this;
      },
      work,
      offWork() {
        this.reset();
        window.removeEventListener('resize', eventListener);
      },
    };
    
    let _instance = null;
    
    function createBubbleInput() {
      if (!_instance) {
        _instance = BubbleInput;
      }
    
      return _instance;
    }
    
    export default createBubbleInput();

    所以, 你只需这么使用, 在应用的入口文件引入BubbleInput, 调用BubbleInput.work()即可;

    // src/index.js
    import BubbleInput from 'bubble-input';
    BubbleInput.work();
    // some-page.js
    import BubbleInput from 'bubble-input';
    BubbleInput
      .setOffsetBottom(someButton.offsetHeight || 0)
      .setTarget(this.refs.container);

    到这里, 相信有些同学肯定已经想到一个问题了, 我在pageA调用BubbleInput.setOffsetBottom(10)设置了offsetBottom, 那如果切换页面到了pageB, 由于BubbleInput是单例的, 所以在pageA设置的offsetBottom也会影响到pageB. 于是我尝试寻找通用的解决方案, 在BubbleInput内部实现监听浏览器url的变化, 调用BubbleInput.reset(). 但是结果是遗憾的:

    1.首先我考虑的是监听浏览器的popstate事件, 但是很遗憾History.pushState()和History.replaceState()并不会触发popstate事件.

    window.addEventListener('popstate', function(event) {
      // 以下代码不会执行
      console.log(event);
    });

    2.然后我考虑尝试从我们公司的技术栈的角度出发解决问题, react-router3.x, react-router4.x, 确实有api.

    // react-router3 我们通过browserHistory.listen()来添加url变化的监听事件
    import { browserHistory } from 'react-router';
    browserHistory.listen(() => {
      BubbleInput.reset();
    });
    // react-router4 我们知道在页面中, 我们可以通过props获取history
    class Page extends React.Component {
      componentDidMount() {
        this.props.history.listen(() => {
          BubbleInput.reset();
        });
      }
    }

    很显然, 虽然以上2种方案能解决问题, 但是不能够达到适配不同框架的效果, 甚至如果我是用了Vue

    最终, 方案我倒是有一个, 可是我不敢用(重写window.history.pushState/replaceState);

    var overwrite = function(type) {
       var fn = history[type];
       return function() {
           var ret = fn.apply(this, arguments);
           var e = new Event(type);
           e.arguments = arguments;
           window.dispatchEvent(e);
    
           BubbleInput.reset();
    
           return ret;
       };
    };
     history.pushState = overwrite('pushState');
     history.replaceState = overwrite('replaceState');

    综合考虑, 最后我妥协了, 希望使用者能帮我完成最后一件事(在需要重置的时候由你手动调用), 比如:

    componentWillUnmount() {
      BubbleInput.reset();
    }

    21fb9ca6-0214-eb11-8da9-e4434bdf6706.png
    好了, 这下真的结束了~~感谢坚持看到底的朋友
    XuZhongqiang/bubble-input​github.com
    23fb9ca6-0214-eb11-8da9-e4434bdf6706.png

    大佬们走过路过点个赞吧, 可以的话帮我点个star.

    TODO:

    1. 发布npm包
    展开全文
  • 前言年后第一天到公司上班,整理一些在移动端H5开发常见的问题给大家做下分享,这里很多是自己在开发过程中遇到的大坑或者遭到过吐糟的问题,希望能给大家带来或多或少的帮助,喜欢的大佬们可以给个小赞,如果有问题...
  • //获取原始窗口的高度 var originalHeight = document.... //软键盘弹起与隐藏 都会引起窗口的高度发生变化 var resizeHeight = document.documentElement.clientHeight || document.body.clientHei..
  • 首先,提一下作者踩过的大坑,那就是使用resize监听窗口大小的做法,经尝试,发现软键盘弹出是,并不会触发这个监听事件。另外一种,是可行的解决方案,那就是通过APP本身去处理这个问题,由APP向JS暴露...
  • iphone端h5页面底部输入框被键盘遮挡问题h5页面在底部放置输入框时,如图 在ios端会出现输入框获取焦点后,中文输入法上横条完全挡住输入框问题,搜索解决方法,在input获取焦点时执行函数,主要scrollIntoView...
  • 问题如图一个表单:在部分android机型上测试点击靠下的输入框时遇到弹出的软键盘挡住输入框问题,ios可自身弹起(ios自身的调整偶尔也会出问题,例如第三方键盘遮挡,原因是第三方输入法的tool bar或者键盘当做...
  • //防止键盘把当前输入框给挡住 $$('input[type="text"],textarea').on('click', function () { var target = this; setTimeout(function(){ target.scrollIntoViewIfNeeded(); },100); }); 部分安卓机型适用。 ...
  • 转载链接:https://segmentfault.com/a/11900000221151771、部分机型软键盘弹起挡住原来的视图...true,表示元素的顶部与当前区域的可见部分的顶部对齐false,表示元素的底部与当前区域的可见部分的尾部对齐Element...
  • 在工作的过程中无论是与原生结合的Hybird的H5页面或者微信公众号页面都会遇到当触发键盘输入行为时,页面窗口往上离开屏幕(与iOS的适配)或者页面高度变小(在微信中打开页面等),现在我们来分析一下各种情况以及解决...
  • 之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家: 系统:Android 条件:当输入框在可视区底部或者偏下的位置 触发条件:输入框获取焦点,...
  • } h5底部输入框被键盘遮挡问题 var oHeight = $(document).height(); //浏览器当前的高度 $(window).resize(function(){ if($(document).height() ){ $("#footer").css("position","static"); }else{ $("#footer")....
  • 键盘高度高于输入框的时候,界面向上拉 从而解决输入框遮挡问题
  • // 键盘调起需要时间延时处理,scrollIntoView是H5的api jquery没有这种方法要用原生获取元素 setTimeout(function () { var element = document.getElementById("chatArea"); element.scrollIntoView(true); //...
  • 解决安卓嵌套H5,软键盘遮挡输入框以及页面华为、小米等底部导航栏遮挡问题
  • 问题描述在移动端 H5 开发中,我们经常会碰到输入框固定在页面底部的布局情况,比如聊天应用,文章评论等。当我们点击输入框输入文字时,系统会弹出虚拟键盘以便输入内容,这个时候会有几种情况出现:输入框随着页面...
  • H5嵌安卓键盘遮挡输入框问题处理 h5嵌入安卓,因为安卓的键盘不占页面位置,当输入框在页面底部的情况下,键盘会遮挡输入框,用户看不到输入内容。 解决办法: 1、页面够长,输入框可滑动到可见位置 2、页面不够长,...
  • 点击输入框,页面弹起输入框整个body的高度就压缩了,并且没有设置滑动,页面的元素挤到了一块,导致下面的购买按钮挤上来了 解决方案: 给整个页面设置min-height,并且设置滑动 body,html { position...
  • h5输入框引起键盘导致体验不好,目前就算微信、知乎、百度等产品也没有很好的技术方案实现,尤其底部固定位置的输入框各种方案都用的前提下体验也并没有很好,这个问题也是老大难问题了。目前在准备一套与native...
  • H5IOS键盘遮住输入框解决方案

    千次阅读 2022-03-11 16:15:21
    H5IOS键盘遮住输入框解决方案
  • 键盘遮盖住了这个输入框,而且此时不能滚动,而按钮的设计为始终置底置底的按钮为fixed布局,观察得知,这样当键盘弹起时,其实页面的高度缩短成类似于这样app中这样,始终置底的元素会遮盖住输入框,并且由于界面...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 497
精华内容 198
关键字:

h5底部输入框被键盘遮挡问题