当前位置: 首页 > 科技观察

通过focusout事件解决IOS键盘收起时界面不归位的问题

时间:2023-03-19 14:51:29 科技观察

使用focusout事件解决IOS键盘收起界面不回原位的问题问题及症状详述如下:页面结构有问题的页面是一个表单结构。即类似于一个div下有4个输入表单的结构,用于用户填写邮寄信息。类似:

截图如下:弹出键盘时,页面自动上移.当用户在手机上输入联系人号码时,会弹出iPhone键盘。这时为了让用户在iPhone上看到电话输入框,整个页面会整体上移(否则键盘会盖住电话输入框)。此时,页面顶部实际上是距离我们视口的一部分距离(我们看到一排输入框从界面上消失了)。键盘收回时无法恢复页面。但是,当用户完成输入并关闭键盘时,虽然键盘收回了,但是页面位置并不会恢复。问题分析其实这是由于IOS收回键盘时页面滚动出视口的部分没有脱落造成的。这时,用户可以用手指将页面向后拖动。但毕竟体验不好。为了解决这个问题,我们可以在用户光标离开输入框时调用window.scrollTo(0,0)来滚动页面对齐视口顶部,从而达到页面归位的效果。所以现在的问题是给表单中的所有4个输入框都添加blur事件,然后在handler中调用window.scrollTo。但是无论是通过Vue的@blur添加,还是通过DOM操作,添加4个事件监听器都不是很优雅。很自然地,我们想到了使用事件代理。事件代理,也就是我们把事件监听器放在最上面的元素上;然后定义一个等待触发的inputBlur函数。
结果发现我们的事件监听无法触发.原因是输入框的blur事件不能冒泡。不能冒泡的解决办法查询后发现规范中focus和blur这两个DOM事件是不能冒泡的。与之类似的还有另外两个事件focusin和focusout可以冒泡。网上有文章提到focusin和focusout是IE浏览器支持的一种DOM事件。其实我们看了MDN文档发现这两个事件已经成为了DOM3规范的一个标准,能够支持的浏览器数量还是挺多的。所以,果断通过这两个事件解决问题,我们改成了focusout。
然后,实现我们的事件处理器:inputBlur(e){//首先,判断触发事件的目标元素是否是输入框,我们只关注输入框的行为。if(e&&e.target&&e.target.tagName&&e.target.tagName.toLowerCase()==='input'){window.scrollTo(0,0);}}这时候我们的问题就解决了,当从输入box输入内容,然后点击键盘关闭键盘,效果符合我们的预期。但是在手机端测试后发现,当我们直接从手机输入框切换到姓名输入框时,页面会抖动。我们继续分析。解决抖动问题其实两个输入框切换时出现抖动的原因也很简单。因为当我们在上面两个输入框之间切换时,页面会先触发电话输入框的blur事件,然后再触发姓名输入框的focus事件。在这种情况下,我们的window.scrollTo(0,0)会在blur时被触发,导致页面向下滚动一点,然后name输入框获得焦点,键盘继续弹出---这就导致了页面再次向上移动。事实上,在两个输入框之间切换时,我们不需要在第一个输入框模糊时触发window.scrollTo行为。那么我们来修改一下我们的代码,这样当输入框切换发生的时候,我们就可以切断第一个输入框的行为。这里我们使用setTimeout来解决:
inputBlur(e){//首先判断触发事件的目标元素是否为输入框,我们只关注输入框的行为。if(e&&e.target&&e.target.tagName&&e.target.tagName.toLowerCase()==='input'){//输入框失去焦点,IOS键盘的滚动部分应该被推出页面才能恢复.将页面滚动到窗口顶部对齐console.log('settimer')this.timer=setTimeout(()=>{console.log('timertriggered')window.scrollTo(0,0);},0)}},inputFocus(e){//如果获得焦点,去掉上一个输入框的定时器(e&&e.target&&e.target.tagName&&e.target.tagName.toLowerCase()==='input'){clearTimeout(这个定时器);}}