最近在作 webapp,遇到了不少移動端兼容的問題,其中一個問題就是:輸入框觸發 focus 後,鍵盤彈出,而後遮住了輸入框。css
而後在Android
和IOS
上,這個問題的表現形式不同,而原生鍵盤和第三方鍵盤也不同,但引發的問題都是同樣的:輸入框被遮住了。html
在鍵盤彈出時,得到焦點的輸入框要在可視區域內,效果以下圖:android
IOS:web
輸入框獲取焦點,鍵盤彈出,webview
高度不會改變,但webview
會往上滾,且最大滾動高度scrollTop
爲鍵盤高度。瀏覽器
點擊鍵盤上的收起按鈕,或者輸入框之外的頁面區域時,輸入框失去焦點,鍵盤收起。app
Android:webapp
輸入框獲取焦點,鍵盤彈出,可是webview
高度會發生改變,高度爲原高度減去軟鍵盤高度。iphone
點擊輸入框之外的區域時,輸入框失去焦點,軟鍵盤收起。而點擊鍵盤上的收起按鈕時,鍵盤收起 ,但輸入框並不會失去焦點,坑。測試
## 解決方案ui
當輸入框位於頁面下部位置時,在IOS
中,webview
會往上滾一段距離,使得獲取焦點的輸入框自動處於可視區,而在Android
裏,只會改變頁面高度,而不會發生焦點元素滾動到可視區的事情。
因此IOS
能夠不用管,而Android
須要在鍵盤彈出的時候,將輸入框滾動到可視區。
首先是要獲取設備類型,經過navigator.userAgent
獲取便可。
function judgeDeviceType() { let deviceType = null; if (!deviceType) { const ua = window.navigator.userAgent.toLocaleLowerCase(); const isIOS = /iphone|ipad|ipod/.test(ua); const isAndroid = /android/.test(ua); deviceType = { isIOS: isIOS, isAndroid: isAndroid }; } return deviceType; }
IOS 能夠經過focus
和blur
事件監聽鍵盤彈出、收起,但 Android 不行,但由於webview
高度會變,因此經過監聽resize
事件解決。
export function listenAndroidKeybord() { const { isAndroid } = judgeDeviceType(); if (isAndroid) { const androidResize = function() { // 將當前焦點元素滾動到可視區 activeElementScrollIntoView(); }; // android 鍵盤彈出、收起,可視區高度會發生變化 window.addEventListener('resize', androidResize, false); return () => { window.removeEventListener('resize', androidResize, false); }; } }
要將元素滾動到可視區,主要有兩個方法:scrollIntoView
和scrollIntoViewIfNeeded
,兼容性在移動端都很不錯。
function activeElementScrollIntoView() { const activeEl = document.activeElement; if ( activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' ) { window.setTimeout(() => { if ('scrollIntoView' in activeEl) { activeElt.scrollIntoView(); } else { activeEl.scrollIntoViewIfNeeded(); } }, 100); } }
以上代碼能夠說解決了大部分瀏覽器鍵盤遮擋問題了,但我用本身的小米手機自帶的小米瀏覽器測試時,出了問題,鍵盤彈出,頁面紋絲不動,手動去拖,有時行,有時不行。
搞了好久,發現了兩個問題,我這手機上自帶的小米瀏覽器,userAgent 上沒有帶Android
標識,但有MiuiBrowser
標識,坑。而後,頁面有時能拖動,有時不能拖動,我猜應該是webview
的可視區高度變化有問題,或者是個人代碼監聽resize
致使有問題。
解決方案
增長設備類型判斷
const ua = window.navigator.userAgent.toLocaleLowerCase(); const isMiuiBrowser = /miuibrowser/.test(ua);
經過監聽focus
和blur
事件來監聽鍵盤彈出、收起,而後給body
加高度
body, html { height: 100%; }
function listenMiuiBrowserKeybord() { const { isMiuiBrowser } = judgeDeviceType(); if (isMiuiBrowser) { const inputFocus = function() { document.body.style.marginBottom = '50px'; activeElementScrollIntoView(); }; const inputBlur = function() { document.body.style.marginBottom = '0px'; activeElementScrollIntoView(); }; let $inputs = document.getElementsByTagName('input'); for (let i = 0; i < $inputs.length; i++) { $inputs[i].addEventListener('focus', inputFocus, false); $inputs[i].addEventListener('blur', inputBlur, false); } return () => { for (let i = 0; i < $inputs.length; i++) { $inputs[i].removeEventListener('focus', inputFocus, false); $inputs[i].removeEventListener('blur', inputBlur, false); } }; } }
坑點:這種方案雖然解決了彈出問題,但點擊鍵盤收起按鈕,Android 下輸入框並不會失去焦點,須要失去焦點才能讓 body 增長的高度變爲 0。
解決方案並不完善,踩坑路漫漫。