最近一个项目需要做一个留言的功能,需求很简单,点击留言按钮底部弹出留言框,然后发送留言给后台就行了,还有一个就是页面要实现复制功能。讨论需求的时候我预想到了手机上软键盘的弹出可能会有一些bug,不过需求比较急,就想着先做完再来改呗。很快做完之后,在电脑上测了没毛病,提测之后,bug刷刷的就来了:

1、手机ios或者Android微信,点击留言弹出输入框,点击完成或者收起按钮,或者安卓手机点击自带返回键,需要完整隐藏输入框;

2、手机ios或者Android微信,点击留言弹出输入框,滑动屏幕都要完整隐藏输入框;

3、IOS手机的复制按钮,点击后屏幕底部有弹框闪出;

4、手机ios或者Android微信,PC微信H5页面在页面下半部分点击复制,页面会跳转到顶部;

5、手机ios,点击留言,输入框会抖动;

6、手机ios点击留言弹出输入框后,此时点击屏幕关闭了输入框,再次点击留言按钮没有影响,直接界面卡死;

可以看到除了3、4是复制相关的,其他都是软键盘相关的,而这些还只是众多bug中有代表性的。。。开始改吧。

先看3、4的复制bug,一说到复制第一反应就是document.execCommand("Copy"),毕竟不需要任何其他组件,简单粗暴,可是在移动端ios出现了弹框瞬间闪出的bug,代码如下:

const btn = document.querySelector('#btn');
btn.addEventListener('click',() => {const input = document.createElement('input');document.body.appendChild(input);input.setAttribute('value', '听说你想复制我');input.select();if (document.execCommand('copy')) {document.execCommand('copy');console.log('复制成功');}document.body.removeChild(input);
})

在Chrome下调试的时候,这个方法时完美运行的。然后到了移动端调试的时候,坑就出来了。

对,没错,就是你,ios。。。

1、点击复制时屏幕下方会出现白屏抖动,仔细看是拉起键盘又瞬间收起

知道了抖动是由于什么产生的就比较好解决了。既然是拉起键盘,那就是聚焦到了输入域,那只要让输入域不可输入就好了,在代码中添加 input.setAttribute('readonly', 'readonly'); 使这个 <input> 是只读的,就不会拉起键盘了。

2、无法复制

这个问题是由于 input.select() 在ios下并没有选中全部内容,我们需要使用另一个方法来选中内容,这个方法就是 input.setSelectionRange(0, input.value.length);。

修改后的代码如下:

const btn = document.querySelector('#btn');
btn.addEventListener('click',() => {const input = document.createElement('input');input.setAttribute('readonly', 'readonly');input.setAttribute('value', 'hello world');document.body.appendChild(input);input.setSelectionRange(0, 9999);if (document.execCommand('copy')) {document.execCommand('copy');console.log('复制成功');}document.body.removeChild(input);
})

问题又来了,用了setSelectionRange这个方法,在pc端都不能复制了,然后我还是把它改成了input.select(),pc和移动端都能复制,不过ios里面的那个键盘突然闪出问题并不会因为设置了readonly而解决。而且关键是:手机ios或者Android微信,PC微信H5页面在页面下半部分点击复制,页面会跳转到顶部,所以这个execCommand方法并不好用,既然这样那我还是用个组件呗,直接就选择了clipboard.js,官网直接选择了通过属性复制,完美解决一切复制的bug,还是组件真香。

好了,接着看看关于软键盘的几个bug:

1、手机ios或者Android微信,点击留言弹出输入框,点击完成或者收起按钮,或者安卓手机点击自带返回键,需要完整隐藏输入框;

2、手机ios或者Android微信,点击留言弹出输入框,滑动屏幕都要完整隐藏输入框;

5、手机ios,点击留言,输入框会抖动;

6、手机ios点击留言弹出输入框后,此时点击屏幕关闭了输入框,再次点击留言按钮没有影响,直接界面卡死;

由于前面用了组件真香,本来一个下面固定的弹出框也准备自己写的,后来想到fixed在ios的各种坑想了还是用弹出框界用的比较多的layer吧,页面的一些toast提示也可以用layer.msg(),瞬间写好弹框:

    //点击留言$(document).on('click','.leaveMsgBtn',function(){layer.open({type: 1, //页面层content: $('#leaveMsgPop'),area: ['100%','auto'],title: false,//不显示标题closeBtn: 0,//不显示右上角关闭按钮scrollbar: false,//页面滚动条隐藏shadeClose:true,//点击遮罩关闭弹出层offset: 'b',//从底部弹出success: function(layero, index){//内容显示$('#leaveMsgPop').css('opacity','1');//隐藏底部按钮$('.footer').hide();$('#msgTextarea',layero).focus(); $('#msgTextarea',layero).on('input',function(){if($(this).val()!=''){$(this).parent().find('.sendMsg').removeClass('disable');$(this).parent().find('.sendMsg').removeAttr('disabled'); }else{$(this).parent().find('.sendMsg').addClass('disable');$(this).parent().find('.sendMsg').attr("disabled","disabled"); }});},end:function(){ //内容隐藏$('#leaveMsgPop').css('opacity','0');//显示底部按钮$('.footer').show();} });});

html代码:

<!-- 弹出框内容 --><div id="leaveMsgPop"><textarea name="" id="msgTextarea" rows="2" maxlength="100" placeholder="商品/客户有问题?请留言..."></textarea><button class="sendMsg disable" disabled>发送</button></div>

效果图:

可以看到就是一个极简单的弹出框,textarea自动focus,在移动端就可以弹出软键盘。

关于这个:1、手机ios或者Android微信,点击留言弹出输入框,点击完成或者收起按钮,或者安卓手机点击自带返回键,需要完整隐藏输入框;

要求点击键盘右上角的那个完成可以收起弹出框,键盘上按键能通过keycode监听到,这个完成按钮都越出键盘外不在五行中了,咋监听呢,通过其他办法咯,发现点击完成键盘就会收起来,那么就去监听键盘收起的状态吧,嗯,ios可以通过focusin和focusout来监听,focusin和focusout支持冒泡,对应focus和blur, 使用focusin和focusout的原因是focusin和focusout可以冒泡,focus和blur不会冒泡,这样就可以使用事件代理,处理多个输入框存在的情况。

在android中,点击键盘上的收起按钮,键盘虽然收起了,输入框仍然处于焦点状态,并没有触发focusout事件。经实践,发现一种变通的方法。通过比较window resize后的clientHeight与最初进来页面时的clientHeight进行对比,如果小于最初的值,那么就可以认为是键盘弹出,否则,认为键盘收起。在android中键盘弹出和收起会改变window的高度,因此监听window的resize。

那么修改后的代码就是:

success: function(layero, index){//内容显示$('#leaveMsgPop').css('opacity','1');//隐藏底部按钮$('.footer').hide();$('#msgTextarea',layero).focus(); $('#msgTextarea',layero).on('input',function(){if($(this).val()!=''){$(this).parent().find('.sendMsg').removeClass('disable');$(this).parent().find('.sendMsg').removeAttr('disabled'); }else{$(this).parent().find('.sendMsg').addClass('disable');$(this).parent().find('.sendMsg').attr("disabled","disabled"); }});//iosif (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {$(document).on('focusin', function () {//软键盘弹出的事件处理});$(document).on('focusout', function () {//软键盘收起的事件处理if($('#msgTextarea',layero).val()==''){layer.close(index);}});}else if (/(Android)/i.test(navigator.userAgent)) {//安卓var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;$(window).on('resize', function () {var nowClientHeight = document.documentElement.clientHeight || document.body.clientHeight;if (clientHeight > nowClientHeight) {//键盘弹出的事件处理}else {//键盘收起的事件处理if($('#msgTextarea',layero).val()==''){layer.close(index);}}});}},

这里还加了个判断,如果你没输入任何内容点击完成那么弹出框就关闭,如果有内容的话就只是收起键盘了,这样第一个bug算是解决了。

2、手机ios或者Android微信,点击留言弹出输入框,滑动屏幕都要完整隐藏输入框;

这个滑动屏幕关闭输入框主要是因为滚动穿透引起的,弹出框出来body就不让它滚动,很容易就想到了给body一个fixed,不过有个问题,就是原来页面滑到了下面,然后出现弹框,如果只是给body一个fixed的话,可以看到遮罩后面的页面一下子滚动到页头,这样的用户体验肯定是不允许的,那就需要保留之前滚到页面某个部分的位置咯。代码如下:

    //禁止滚动var ModalHelper = (function(bodyCls) {var scrollTop; // 在闭包中定义一个用来保存滚动位置的变量return {afterOpen: function() { //弹出之后记录保存滚动位置,并且给body添加.modal-openscrollTop = document.scrollingElement.scrollTop;document.body.classList.add(bodyCls);document.body.style.top = -scrollTop + 'px';},beforeClose: function() { //关闭时将.modal-open移除并还原之前保存滚动位置document.body.classList.remove(bodyCls);document.scrollingElement.scrollTop = scrollTop;}};})('modal-open');

然后,css的代码是:

body.modal-open {position: fixed;width: 100%;
}

这样就保证遮罩下面的内容不让用户去穿透滚动了。

5、手机ios,点击留言,输入框会抖动;

这个抖动的话完全是我之前自己为了有个从下滑入动画效果,而layer里面正好有anim参数,结果设置了之后,ios键盘出来直接把弹出框往上顶了两下,就出现了所谓的抖动,得,动画去掉吧。

6、手机ios点击留言弹出输入框后,此时点击屏幕关闭了输入框,再次点击留言按钮没有影响,直接界面卡死;

最后这个问题就找了很久,主要是没找到卡死的原因,状况就是我输入框输入了内容,然后点击完成按钮,键盘会收起来,这时候我再点输入框,会发现完全没反应,点击发送按钮也没反应,这就造成了卡死的假象,然后我在测的时候在页面上随便点,发现有时候键盘会出来,有时候会关闭弹出框。多试几次之后,发现点击页面中间,键盘会出来,突然想到一开始键盘出来的时候貌似就是把弹出框顶到了页面中间的位置,问题找到了就好解决了。

这就是ios的一个bug,在弹出框有输入域聚焦的时候,键盘弹出来,然后弹出框会在页面中间位置,这个时候收起了键盘,其实弹出框的输入域的光标还在之前的但是看不见的位置,也就是页面中间,那么就需要在键盘收起来让页面的scrollTop也发生变化了。解决代码如下:

//键盘收起时需要body页面scrollTop改变setTimeout(function () {var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0;window.scrollTo(0, Math.max(scrollHeight - 1, 0));},100);

scrollHeight记载的就是键盘出来前弹出框的scrollTop,键盘出来之后会把弹出框顶上去,那么scrollTop就会发生变化,在收起键盘的时候我们需要scrollTop回到原来的值。

可以看出来这些bug主要都是集中在ios中,安卓对fixed的体验会好很多,好了,最后贴上这段看似简单需求弹出框的完整js代码以做记录:

    //禁止滚动var ModalHelper = (function(bodyCls) {var scrollTop; // 在闭包中定义一个用来保存滚动位置的变量return {afterOpen: function() { //弹出之后记录保存滚动位置,并且给body添加.modal-openscrollTop = document.scrollingElement.scrollTop;document.body.classList.add(bodyCls);document.body.style.top = -scrollTop + 'px';},beforeClose: function() { //关闭时将.modal-open移除并还原之前保存滚动位置document.body.classList.remove(bodyCls);document.scrollingElement.scrollTop = scrollTop;}};})('modal-open');//点击留言$(document).on('click','.leaveMsgBtn',function(){layer.open({type: 1, //页面层content: $('#leaveMsgPop'),area: ['100%','auto'],title: false,//不显示标题closeBtn: 0,//不显示右上角关闭按钮scrollbar: false,//页面滚动条隐藏shadeClose:true,//点击遮罩关闭弹出层offset: 'b',//从底部弹出success: function(layero, index){//内容显示$('#leaveMsgPop').css('opacity','1');//隐藏底部按钮$('.footer').hide();$('#msgTextarea',layero).focus(); $('#msgTextarea',layero).on('input',function(){if($(this).val()!=''){$(this).parent().find('.sendMsg').removeClass('disable');$(this).parent().find('.sendMsg').removeAttr('disabled'); }else{$(this).parent().find('.sendMsg').addClass('disable');$(this).parent().find('.sendMsg').attr("disabled","disabled"); }});//iosif (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {$(document).on('focusin', function () {//软键盘弹出的事件处理//禁止滚动ModalHelper.afterOpen();});$(document).on('focusout', function () {//软键盘收起的事件处理if($('#msgTextarea',layero).val()==''){layer.close(index);//可以滚动ModalHelper.beforeClose();}//键盘收起时需要body页面scrollTop改变setTimeout(function () {var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0;window.scrollTo(0, Math.max(scrollHeight - 1, 0));},100);});}else if (/(Android)/i.test(navigator.userAgent)) {//安卓var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;$(window).on('resize', function () {var nowClientHeight = document.documentElement.clientHeight || document.body.clientHeight;if (clientHeight > nowClientHeight) {//键盘弹出的事件处理//禁止滚动ModalHelper.afterOpen();}else {//键盘收起的事件处理if($('#msgTextarea',layero).val()==''){layer.close(index);//可以滚动ModalHelper.beforeClose();}}});}//禁止滚动ModalHelper.afterOpen();},end:function(){ //内容隐藏$('#leaveMsgPop').css('opacity','0');//显示底部按钮$('.footer').show();//可以滚动ModalHelper.beforeClose();} });});

移动端H5页面关于软键盘的一些踩坑记录相关推荐

  1. 安卓系列手机的H5页面背景色失效的问题(踩坑手机小米8)

    标题安卓系列手机的H5页面背景色失效的问题 今天做手机端,微信小程序开发,根据实际需求,手写一个遮罩层 页面开发完毕之后,进行真机测试,出现问题了,ios是好的 Android一款小米8就不行了 在打 ...

  2. H5页面实现微信分享功能及踩坑历程

    看了官网,写的也挺简单的,也在网上搜索了一些demo,然后开始写: 我理解的误区: 我一直以为可以跟app分享一样,有个分享按钮点击触发分享:搜索了很多博客,得到结论,前几年好像是可以通过按钮引导分享 ...

  3. 移动端h5页面软键盘弹出后 背景图片被顶上去

    移动端h5页面在软键盘弹出后,body的高度被压缩了,就导致原本高度100%的背景图被顶上去一截,需要把div的高度强行设回100%才能解决这个问题 <div class="app&q ...

  4. 移动端H5页面,关闭手机键盘!

    标题 移动端H5页面,关闭手机键盘! 最近在手机端页面遇到当点了输入框后弹出手机键盘,当关闭当前弹框页面后手机键盘未关闭. 解决方案: document.activeElement.blur(); 注 ...

  5. m3u8 video ios h5_移动端H5页面踩坑记

    移动端H5页面踩坑记 移动端的样式问题 「1. 安卓 font-weight:700;以上才被认为是加粗.」 「2. border 在 1px 以内是不被安卓识别的一些 0.5px 的下划线怎么搞?」 ...

  6. 【通用CSS模板】移动端H5页面统一样式.css

    /*移动端H5页面统一样式----------------------------------------*/ @charset "UTF-8"; body, html, li, ...

  7. 移动端H5页面高清多屏适配方案

    背景 开发移动端H5页面 面对不同分辨率的手机 面对不同屏幕尺寸的手机 视觉稿 在前端开发之前,视觉MM会给我们一个psd文件,称之为视觉稿. 对于移动端开发而言,为了做到页面高清的效果,视觉稿的规范 ...

  8. H5页面随机数字键盘支付页面

    H5页面随机数字键盘支付页面 有个H5支付的业务需要随机数字的键盘 参考了下文:https://blog.csdn.net/Mr_Smile2014/article/details/52473351 ...

  9. 教你如何用 lib-flexible 实现移动端H5页面适配

    前话 好久没写教程了(可能会误导新手的菜鸟教程( ̄▽ ̄)"). 这是我的github,欢迎前端大大们和我一起学习交流 https://github.com/pwcong 最近入职公司做前端实 ...

最新文章

  1. Jenkins构建Maven聚合工程,指定构建子模块
  2. dw的css样式怎么删除掉,三种方法教你DreamWeaver下如何应用CSS样式
  3. DataFrame的copy的用法
  4. Sphinx武林秘籍(上)
  5. linux压缩命令 实例子,Linux下的tar压缩解压缩命令详解及使用实例分析
  6. C# 接口持有结构体会导致装箱问题
  7. PCA、SVD、ZCA白化理论与实现
  8. Taro+react开发(18)--定义变量值
  9. 终于有人把Elasticsearch原理讲明白了!
  10. python爬取新闻存入数据库_python 爬取古诗文存入mysql数据库的方法
  11. 计算机信息机房,计算机信息中心机房建设标准
  12. Java面向对象编程,绘制思维导图(全面详细整理)
  13. QCC3040/QCC3020主要差异对比
  14. 计算机无法删除ie,Win7系统IE11 IE10 IE9强制卸载工具方法(解决IE无法卸载)
  15. C-CCSK云计算安全知识认证
  16. html图片滚动首尾互联,Flash几张图片首尾连接循环滚动
  17. 【应急基础】————13、VBS遍历目录获取文件Hash
  18. 8款能让打工人效率原地飞起的神仙软件,含泪公开
  19. iOS开发之3D Touch(快速添加3D Touch功能)
  20. 数据时代大数据管理,主要有哪些策略?

热门文章

  1. WebSocket 原理 1
  2. 高等数学--导数、偏导数、梯度简介
  3. Java求两点的中点坐标_计算两点坐标距离与中点坐标
  4. Spring集成Activemq使用
  5. 算法设计与分析基础 第一章谜题
  6. 发展智慧城市,需要重点解决哪三大问题?
  7. 梆梆加固的病毒分析-破解篇
  8. CENTOS上的网络安全工具(十六)容器特色的Linux操作
  9. 这10种情况的房屋需做结构检测鉴定了
  10. NRF24L01实验(STM32F103ZE与STM32L475ZE通信)