前言 最近在做 webapp,遇到了很多移動端相容的問題,其中一個問題就是:輸入框觸發 focus 後,鍵盤彈出,然後遮住了輸入框。 然後在 和`IOS`上,這個問題的表現形式不一樣,而原生鍵盤和第三方鍵盤也不一樣,但引起的問題都是一樣的:輸入框被遮住了。 需要的效果 在鍵盤彈出時,獲得焦點的輸入框 ...
前言
最近在做 webapp,遇到了很多移動端相容的問題,其中一個問題就是:輸入框觸發 focus 後,鍵盤彈出,然後遮住了輸入框。
然後在Android
和IOS
上,這個問題的表現形式不一樣,而原生鍵盤和第三方鍵盤也不一樣,但引起的問題都是一樣的:輸入框被遮住了。
需要的效果
在鍵盤彈出時,獲得焦點的輸入框要在可視區域內,效果如下圖:
鍵盤彈出、收起的表現
IOS:
輸入框獲取焦點,鍵盤彈出,
webview
高度不會改變,但webview
會往上滾,且最大滾動高度scrollTop
為鍵盤高度。點擊鍵盤上的收起按鈕,或者輸入框以外的頁面區域時,輸入框失去焦點,鍵盤收起。
Android:
輸入框獲取焦點,鍵盤彈出,但是
webview
高度會發生改變,高度為原高度減去軟鍵盤高度。點擊輸入框以外的區域時,輸入框失去焦點,軟鍵盤收起。而點擊鍵盤上的收起按鈕時,鍵盤收起 ,但輸入框並不會失去焦點,坑。
解決方案
當輸入框被擋住,在IOS
中,webview
會往上滾一段距離,使得獲取焦點的輸入框自動處於可視區,而在Android
里,只會改變webview
高度,而不會發生焦點元素滾動到可視區的事情。
所以IOS
可以不用管,而Android
需要在鍵盤彈出的時候,將輸入框滾動到可視區。
獲取設備類型
首先是要獲取設備類型,通過navigator.userAgent
獲取即可。
const judgeDeviceType = (() => {
let deviceType = null;
return () => {
if (!deviceType) {
const ua = window.navigator.userAgent.toLocaleLowerCase();
const isIOS = /iphone|ipad|ipod/.test(ua);
const isAndroid = /android/.test(ua);
const isMiuiBrowser = /miuibrowser/.test(ua);
deviceType = {
isIOS: isIOS,
isAndroid: isAndroid,
isMiuiBrowser: isMiuiBrowser
};
}
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);
}
}
MiuiBrowser
以上代碼可以說解決了大部分瀏覽器鍵盤遮擋問題了,但我用自己的小米手機自帶的小米瀏覽器測試時,出了問題,鍵盤彈出,頁面紋絲不動,手動去拖,有時行,有時不行。
搞了很久,發現了兩個問題,我這手機上自帶的小米瀏覽器,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。
備註
解決方案並不完善,踩坑路漫漫。