前言这是一个由来已久的历史问题,而且浏览器还没有给出这样一个普遍性问题的解决方案,什么?没有计划谈什么?别着急,别着急,我们来看看软键盘高度和输入法的问题。让我们看一个简短的视频来理解这个问题。问题描述:当操作员进行输入操作时,弹出的软键盘原来的输入框被挡住了,导致操作员看不到操作结果上一篇解决方案上一篇解决方案:修改网站的页面布局,例如这个例子,twitter尽量把input放在中间上方,从layout上避免这种问题,在一些指定的设备和浏览器中异步获取window.innerHeight,前后比较得到键盘的高度我们来个再看一个常见的输入框的页面布局:在这个场景中,输入框位于页面底部,当软键盘弹出时,整个视图窗口向上滚动,到达底部时停止。恰好我们用h5模拟这个效果的时候,勉强做到了。这是因为第一次fuse到输入框时会弹出软键盘,浏览器会向上滚动页面以确保输入可见。这个特性和document.body.scrollIntoViewIfNeeded方法是一致的,但是当你的body可滚动高度超过窗口高度时就会出现另一个问题:固定元素会如下图那样随着页面滚动,所以浏览器关心的只是输入是否被覆盖?实际上是输入中的光标位置!那么这就解释了为什么输入框在底部的时候只是勉强完成,因为当输入在页面底部时,软键盘会弹出来覆盖输入,所以浏览器会向上滚动到该位置输入框可见的地方。但是无法实现下图所示的效果,因为输入框下面有一排工具栏,也就是说输入框不在底部位置,所以浏览器只会保证输入的是滚动到可见位置时到达。可见,但浏览器不考虑工具栏是否可见。分析IOING的解决方案。综观以上两种布局方案存在的问题,都不能完美解决输入被键盘挡住,底部footer无法抬起的问题。没有出路吗?当然,号称可以让HTML5性能更接近Native的IOING引擎肯定有解决办法。让我们首先看一下IOING中输入的性能。我们可以看到页面在输入过程中通过滚动使光标保持在可见区域的中心。位置,所以这里需要提一个知识点:获取输入光标的实时位置,当然这也是一个曲折的过程,这里就不展开话题了,继续说三个主要的原题前面提到的传统解决方案:第一个是将input布局尽可能放在页面顶部。显然这不是我们想要的,所以拒绝把input放到底部来完成固定footer的效果,但是页面的高度不能超过window的高度,我们可以通过自己做一个来去掉这个限制做了滚动控制。现在需要解决的技术点是实现一个模拟滚动控件,通过比较软键盘弹出前后window.innerHeight的高度差来获取键盘高度,然后根据这个Height来实现剧中底部定位和输入,但是这种方式受限于不同设备平台的支持综上所述,我们总结一下我们需要解决的思路和步骤。让我们看看下面的图片。当键盘弹出时,键盘高度=否可见窗口高度的等式是有条件的,只有输入在底部时该等式才有效(这就是上面提到的scrollIntoViewIfNeeded的原因)。思考:如果我们能使这个等式成立,并且能得到不可见的Position高度,我们能不能得到键盘的高度呢?下面我们一步步梳理一下实现的思路1.需要在虚拟滚动中放置内容,可以像下面这样在IOING中创建一个虚拟滚动区域页面内容传统页面可以使用WebKit私有属性“-webkit-overflow-scrolling:touch”允许独立滚动区域和触摸回弹,或者使用iScroll.js等第三方库来完成,但是需要注意的是iScroll使用不当可能会导致性能问题2.获取光标在屏幕上的位置3.当光标为焦点时,弹出键盘。如果输入被阻塞,页面会滚动,但是滚动量是不确定的,所以我们可以强制滚动到Bottom,即键盘完全弹出后,窗口主动向上滚动到窗口高度。实际上,窗口只能向上滚动到底部位置,不能再向上滚动。此时获取到的页面的top.scrollY就是实际的键盘高度推导公式:可见区域中心位置=键盘高度+(窗口高度-键盘高度)/2应滚动距离=可见区域中心位置-光标offsetTop-(cursorisblocked?keyboardheight:0)当然actual操作需要更多细节,把这部分逻辑实现的源码贴在IOING://部分源码在IOING//dom是input元素//scroll是滚动容器函数的Scroll对象scrollTo(y,_y,t,s,r){r=r==undefined?1:ry=y==undefined?top.scrollY:yif(r==1?y>_y:y<_y)返回s=s==undefined?数学.abs((_y-y)/t*17.6):srAF(function(){top.scrollTo(0,y+=r*s)scrollTo(y,_y,t,s,r)})}functionvisibility(){if(this.movi??ng||this.wheeling){vartop=dom.offset().topvarheight=dom.offsetHeightvarviewTop=keyboardHeight+scrollOffsetTopvarviewBottom=factWindowHeight-scrollOffsetBottomif(top+height<=viewTop||top>=viewBottom){dom.blur()}}}函数refreshCursor(){rAF(function(){dom.getSelectionRangeInsert('')})}functiongetScroll(){varscroller=reactScroller||dom.closest('scroll')scroll=scroller?scroller.scrollEvent:nullif(type==1){minScrollY=scroll.minScrollY}}functiongetViewOffset(){//android:(top.scrollY==0?keyboardHeight:0)viewOffset=viewCenter-rangeOffset.top-(top.scrollY==0?keyboardHeight:0)+(that.module.config.sandbox?keyboardHeight:0)returnviewOffset}functionkeyboardUp(e){getScroll(1)if(!scroll)return//刷新光标{{if(device.os.ios&&device.os.iosVersion<12){scroll.on('scrollscrollend',refreshCursor)}//}}if(normal)返回函数upend(e){window.keyboard.height=keyboardHeight=top.scrollY||factWindowHeight-top.innerHeight//改变minScrollYscroll.minScrollY=minScrollY+keyboardHeightscroll.options.minScrollY=scroll.minScrollY//光标位置rangeOffset=dom.getSelectionRangeOffset()//可见视图的中心viewWrapper=factWindowHeight-keyboardHeight-scrollOffsetTop-scrollOffsetBottomviewCenter=keyboardHeight+viewWrapper.sc,2scrolly(getViewOffset(),600,null,false)//blurscroll.on('scroll',visibility)window.trigger('keyboardup',{height:keyboardHeight})if(reactResize){scrollTo(null,0,300,null,-1)}}setTimeout(function(){top.one('scrollend',upend)//没有滚动setTimeout(function(){if(keyboardHeight==0)upend()},300)//```oldvaroffset=0if(device.os.mobileSafari&&device.os.iosVersion<12){offset=24*viewportScale}//滚动到底部scrollTo(null,viewportHeight-offset,300,null,1)},300)}functionkeyboardDown(){getScroll()if(!scroll)return//```old:刷新光标{{if(device.os.ios&&device.os.iosVersion<11){scroll.off('scrollscrollend',refreshCursor)}//}}if(normal)returnif(keyboardHeight==0)returnfalsetop.scrollTo(0,0)scroll.wrapper.scrollTop=0//改变minScrollYscroll.minScrollY=minScrollYscroll.options.minScrollY=minScrollYscroll.off('scroll',visibility)scroll._refresh()window.keyboard.height=keyboardHeight=0}functionselectionRange(e){getScroll()if(!scroll)return//非箭头按键取消if(e.type=='keyup'&&![8,13,37,38,39,40].consistOf(e.keyCode))return//重新设置光标位置if(reactOffset){rangeOffset=dom.getSelectionRangeOffset()}elseif(reactPosition){rangeOffset=dom.getSelectionRangePosition()}if(reactOrigin&&rangeOffset){rangeOffset.each(function(i,v){scope.setValueOfHref(reactOrigin+'.'+i,v)})}如果(normal)return//使光标居中if(e.type=='input'&&e.timeStamp-timeStamp<2000)returnif(!scroll||!viewCenter)returnif(!reactOffset){rangeOffset=dom.getSelectionRangeOffset()}timeStamp=e.timeStampscroll.scrollBy(0,getViewOffset(),400,null,false)}dom.on('click',checkChange)dom.on('focus',keyboardUp)dom.on('blur',keyboardDown)dom.on('focuskeyupinputpastemouseup',selectionRange)})其他小细节及注意事项:safari会受到浏览器底部导航栏的影响,导致错误20多pixels,对于safari需要考虑的是输入光标在进行transform3d变换时,光标会停滞。需要执行游标刷新操作。当输入被操作员主动滑出可见区域时,键盘应该收回。否则,scrollIntoViewIfNeeded的效果会是导致窗口滚动空白的问题,最终的结论是:获取键盘的高度只是我们的表象。真正解决html5带来的各种问题才是我们的研究课题,唯有清理这些布局杀手h5距离赶上Native又近了一步!最后,最后,让我po到最后。在IOING中我们需要做什么来完成这一步呢?就这么简单,IOING中的input可以默认自动居中。如果要取消这个功能,就这样写,当然你也可以设置centertobottom/Offsetpositionrelativetothetop可以在输入过程中实时输出光标位置,并将位置信息赋值给数据源对象当前光标位置:左:{test.range.left},上:{test.range.top}
使用js获取键盘高度的方法//键盘抬起时为键盘高度,不抬起时为0。console.log(window.keyboard.height)//通过键盘弹起事件获取window.on('keyboardup',function(e){console.log(e.height)})//键盘弹下事件window.on('keyboarddown',function(e){console.log(e.height)//0})详细文档传送门:http://ioing.com/#docs-dom-inputGitHub传送门:https://github.com/IOING/IOING