可能這些是你想要的H5軟鍵盤兼容方案
前言
輸入框獲取焦點晦鞋,軟鍵盤彈起,要求輸入框吸附(或頂)在輸入法框上棺克。需求很明確悠垛,看似很簡單,其實不然娜谊。從實驗過一些機型上看确买,發(fā)現(xiàn)主要存在以下問題:
在?Android?和?IOS?上,獲知軟鍵盤彈起和收起狀態(tài)存在差異纱皆,且頁面?webview?表現(xiàn)不同拇惋。
在IOS12?上,微信版本?v6.7.4?及以上抹剩,輸入框獲取焦點,鍵盤彈起蓉坎,頁面(webview)整體往上滾動澳眷,當(dāng)鍵盤收起后,不回到原位蛉艾,導(dǎo)致鍵盤原來所在位置是空白的钳踊。
在?IOS?上,使用第三方輸入法勿侯,高度計算存在偏差拓瞪,導(dǎo)致在有些輸入法彈起,將輸入框擋住一部分助琐。
在有些瀏覽器上使用一些操作技巧祭埂,還是存在輸入框被輸入法遮擋。
下面就上述發(fā)現(xiàn)的問題兵钮,逐個探索一下解決方案蛆橡。
獲知軟鍵盤彈起和收起狀態(tài)
獲知軟鍵盤的彈起還是收起狀態(tài)很重要,后面的兼容處理都要以此為前提掘譬。然而泰演,H5?并沒有直接監(jiān)聽軟鍵盤的原生事件,只能通過軟鍵盤彈起或收起葱轩,引發(fā)頁面其他方面的表現(xiàn)間接監(jiān)聽睦焕,曲線救國藐握。并且,在?IOS?和?Android上的表現(xiàn)不盡相同垃喊。
IOS 軟鍵盤彈起表現(xiàn)
在?IOS?上猾普,輸入框(input、textarea?或 富文本)獲取焦點缔御,鍵盤彈起抬闷,頁面(webview)并沒有被壓縮,或者說高度(height)沒有改變耕突,只是頁面(webview)整體往上滾了笤成,且最大滾動高度(scrollTop)為軟鍵盤高度。
Android 軟鍵盤彈起表現(xiàn)
同樣眷茁,在?Android?上炕泳,輸入框獲取焦點,鍵盤彈起上祈,但是頁面(webview)高度會發(fā)生改變培遵,一般來說,高度為可視區(qū)高度(原高度減去軟鍵盤高度)登刺,除了因為頁面內(nèi)容被撐開可以產(chǎn)生滾動籽腕,webview?本身不能滾動。
IOS 軟鍵盤收起表現(xiàn)
觸發(fā)軟鍵盤上的“收起”按鈕鍵盤或者輸入框以外的頁面區(qū)域時纸俭,輸入框失去焦點皇耗,軟鍵盤收起。
Android 軟鍵盤收起表現(xiàn)
觸發(fā)輸入框以外的區(qū)域時揍很,輸入框失去焦點郎楼,軟鍵盤收起。但是窒悔,觸發(fā)鍵盤上的收起按鈕鍵盤時呜袁,輸入框并不會失去焦點,同樣軟鍵盤收起简珠。
監(jiān)聽軟鍵盤彈起和收起
綜合上面鍵盤彈起和收起在?IOS?和?Android?上的不同表現(xiàn)阶界,我們可以分開進行如下處理來監(jiān)聽軟鍵盤的彈起和收起:
在?IOS?上,監(jiān)聽輸入框的?focus?事件來獲知軟鍵盤彈起聋庵,監(jiān)聽輸入框的?blur?事件獲知軟鍵盤收起荐操。
在?Android?上,監(jiān)聽?webview?高度會變化珍策,高度變小獲知軟鍵盤彈起托启,否則軟鍵盤收起。
//?判斷設(shè)備類型
var?judgeDeviceType?=?function?()?{
??var?ua?=?window.navigator.userAgent.toLocaleLowerCase();
??var?isIOS?=?/iphone|ipad|ipod/.test(ua);
??var?isAndroid?=?/android/.test(ua);
??return?{
????isIOS:?isIOS,
????isAndroid:?isAndroid
??}
}()
//?監(jiān)聽輸入框的軟鍵盤彈起和收起事件
function?listenKeybord($input)?{
??if?(judgeDeviceType.isIOS)?{
????//?IOS?鍵盤彈起:IOS?和?Android?輸入框獲取焦點鍵盤彈起
????$input.addEventListener('focus',?function?()?{
??????console.log('IOS?鍵盤彈起啦攘宙!');
??????//?IOS?鍵盤彈起后操作
????},?false)
????//?IOS?鍵盤收起:IOS?點擊輸入框以外區(qū)域或點擊收起按鈕屯耸,輸入框都會失去焦點拐迁,鍵盤會收起,
????$input.addEventListener('blur',?()?=>?{
??????console.log('IOS?鍵盤收起啦疗绣!');
??????//?IOS?鍵盤收起后操作
????})
??}
??//?Andriod?鍵盤收起:Andriod?鍵盤彈起或收起頁面高度會發(fā)生變化线召,以此為依據(jù)獲知鍵盤收起
??if?(judgeDeviceType.isAndroid)?{
????var?originHeight?=?document.documentElement.clientHeight?||?document.body.clientHeight;
????window.addEventListener('resize',?function?()?{
??????var?resizeHeight?=?document.documentElement.clientHeight?||?document.body.clientHeight;
??????if?(originHeight?<?resizeHeight)?{
????????console.log('Android?鍵盤收起啦!');
????????//?Android?鍵盤收起后操作
??????}?else?{
????????console.log('Android?鍵盤彈起啦多矮!');
????????//?Android?鍵盤彈起后操作
??????}
??????originHeight?=?resizeHeight;
????},?false)
??}
}
var?$inputs?=?document.querySelectorAll('.input');
for?(var?i?=?0;?i?<?$inputs.length;?i++)?{
??listenKeybord($inputs[i]);
}
彈起軟鍵盤始終讓輸入框滾動到可視區(qū)
有時我們會做一個輸入表單缓淹,有很多輸入項,輸入框獲取焦點塔逃,彈起軟鍵盤讯壶。當(dāng)輸入框位于頁面下部位置時,在?IOS?上湾盗,會將?webview?整體往上滾一段距離伏蚊,使得該獲取焦點的輸入框自動處于可視區(qū),而在?Android?則不會這樣格粪,它只會改變頁面高度躏吊,而不會去滾動到當(dāng)前焦點元素到可視區(qū)。
由于上面已經(jīng)實現(xiàn)監(jiān)聽?IOS?和?Android?鍵盤彈起和收起帐萎,在這里比伏,只需在?Android?鍵盤彈起后,將焦點元素滾動(scrollIntoView())到可視區(qū)疆导。查看效果凳怨,可以戳這里。
//?獲取到焦點元素滾動到可視區(qū)
function?activeElementScrollIntoView(activeElement,?delay)?{
??var?editable?=?activeElement.getAttribute('contenteditable')
??//?輸入框是鬼、textarea或富文本獲取焦點后沒有將該元素滾動到可視區(qū)
??if?(activeElement.tagName?==?'INPUT'?||?activeElement.tagName?==?'TEXTAREA'?||?editable?===?''?||?editable)?{
????setTimeout(function?()?{
??????activeElement.scrollIntoView();
????},?delay)
??}
}
//?...
//?Android?鍵盤彈起后操作
activeElementScrollIntoView($input,?1000);
//?...
喚起純數(shù)字軟鍵盤
上面的表單輸入框有要求輸入電話號碼,類似這樣就要彈出一個數(shù)字軟鍵盤了紫新,既然說到了軟鍵盤兼容均蜜,在這里就安插一下。比較好的解決方案如下:
<p>請輸入手機號</p>
<input?type="tel"?novalidate="novalidate"?pattern="[0-9]*"?class="input">
type="tel"芒率, 是?HTML5?的一個屬性囤耳,表示輸入框類型為電話號碼,在?Android?和?IOS?上表現(xiàn)差不多偶芍,都會有數(shù)字鍵盤充择,但是也會有字母,略顯多余匪蟀。
pattern="[0-9]"椎麦,?pattern?用于驗證表單輸入的內(nèi)容,通常HTML5?的?type?屬性材彪,比如?email观挎、tel琴儿、number、data類嘁捷、url?等造成,已經(jīng)自帶了簡單的數(shù)據(jù)格式驗證功能了,加上?pattern后雄嚣,前端部分的驗證更加簡單高效了晒屎。IOS?中,只有?[0-9]*?才可以調(diào)起九宮格數(shù)字鍵盤缓升,\d?無效鼓鲁,Android 4.4?以下(包括X5內(nèi)核),兩者都調(diào)起數(shù)字鍵盤仔沿。
novalidate="novalidate"坐桩,novalidate?屬性規(guī)定當(dāng)提交表單時不對其進行驗證,由于?pattern?校驗兼容性不好封锉,可以不讓其校驗绵跷,只讓其喚起純數(shù)字鍵盤,校驗工作由?js?去做成福。
兼容 IOS12 + V6.7.4+
如果你在用?IOS12?和?V6.7.4+版本的微信瀏覽器打開上面表單輸入的demo?碾局,就會驚奇的發(fā)現(xiàn)鍵盤收起后,原本被滾動頂起的頁面并沒有回到底部位置奴艾,導(dǎo)致原來鍵盤彈起的位置“空”了净当。
其實這是?Apple?在?IOS?的?bug,會出現(xiàn)在所有的?Xcode10?打包的IOS12?的設(shè)備上蕴潦。微信官方已給出解決方案像啼,只需在軟鍵盤收起后,將頁面(webview)滾回到窗口最底部位置(clientHeight位置)潭苞。修復(fù)后的上面表單輸入?demo?可以戳這里
console.log('IOS?鍵盤收起啦忽冻!');
//?IOS?鍵盤收起后操作
//?微信瀏覽器版本6.7.4+IOS12會出現(xiàn)鍵盤收起后,視圖被頂上去了沒有下來
var?wechatInfo?=?window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i);
if?(!wechatInfo)?return;
var?wechatVersion?=?wechatInfo[1];
var?version?=?(navigator.appVersion).match(/OS?(\d+)_(\d+)_?(\d+)?/);
if?(+wechatVersion.replace(/\./g,?'')?>=?674?&&?+version[1]?>=?12)?{
??setTimeout(function?()?{
????window.scrollTo(0,?Math.max(document.body.clientHeight,?document.documentElement.clientHeight));
??})
}
兼容第三方輸入法
上面說了那么多此疹,其實已經(jīng)把?H5?聊天輸入框的坑填了一大半了僧诚,接下來就先看下聊天輸入框的基本HTML結(jié)構(gòu)
<div?class="chat__content">
??<div>
????<p>一些聊天內(nèi)容1</p>
??</div>
??<!--?省略幾千行聊天內(nèi)容?-->
</div>
<div?class="input__content">
??<div?class="input"?contenteditable="true"></div>
??<button>發(fā)送</button>
</div>
樣式
/*?省略一些樣式?*/
.chat__content?{
??height:?calc(100%?-?40px);
??margin-bottom:?40px;
??overflow-y:?auto;
??overflow-x:?hidden;
}
.input__content?{
??display:?flex;
??height:?40px;
??position:?absolute;
??left:?0;
??right:?0;
??bottom:?0;
??align-items:?center;
}
/*?省略一些樣式?*/
很簡單,就是劃分內(nèi)容區(qū)和輸入?yún)^(qū)蝗碎,輸入?yún)^(qū)是絕對定位湖笨,按照上面表單輸入demo?的做法,確實大部分?Android?瀏覽器是沒問題的蹦骑,但是測試在IOS?上慈省,UC?瀏覽器配合原生輸入法和第三方輸入法(比如搜狗輸入法),輸入框都會被完全擋酌吖健辫呻;QQ?瀏覽器或微信瀏覽器清钥,配合第三方輸入法,輸入框會被遮住一半放闺;百度瀏覽器配合第三方輸入法輸入框也會被完全遮住祟昭。查看效果可以用相應(yīng)瀏覽器中訪問這里。
在?UC?瀏覽器上怖侦,軟鍵盤彈起后篡悟,瀏覽器上面的標題欄高度就有個高度變小延時動態(tài)效果,這樣導(dǎo)致?webview?往下滾了一點匾寝,底部輸入框滾到了非可視區(qū)搬葬。
? 而對于第三方輸入法,猜測本身是由于輸入法面板彈起后高度計算有誤艳悔,導(dǎo)致?webview?初始滾動定位有誤急凰。其實這兩點都是?webview?滾動不到位造成的〔履辏可以讓軟鍵盤彈起后抡锈,讓焦點元素再次滾到可視區(qū),強迫?webview滾到位乔外。
???console.log('Android?鍵盤彈起啦床三!');
???//?Android?鍵盤彈起后操作
???activeElementScrollIntoView($input,?1000);
兼容 Android 小米瀏覽器的 Hack 方案
在?Android?的小米瀏覽器上,應(yīng)用上面的方案杨幼,發(fā)現(xiàn)聊天輸入框還是被遮擋得嚴嚴實實撇簿,scrollIntoView()?仍然紋絲不動。所以猜測差购,其實是滾到底了四瘫,軟鍵盤彈起,頁面實現(xiàn)高度大于可視區(qū)高度欲逃,這樣只能在軟鍵盤彈起后找蜜,強行增加頁面高度,使輸入框可以顯示出來暖夭。綜合上面兼容第三方輸入法,查看效果可以戳這里
???//?Andriod?鍵盤收起:Andriod?鍵盤彈起或收起頁面高度會發(fā)生變化撵孤,以此為依據(jù)獲知鍵盤收起
???if?(judgeDeviceType.isAndroid)?{
?????var?originHeight?=?document.documentElement.clientHeight?||?document.body.clientHeight;
?????window.addEventListener('resize',?function?()?{
???????var?resizeHeight?=?document.documentElement.clientHeight?||?document.body.clientHeight;
???????if?(originHeight?<?resizeHeight)?{
?????????console.log('Android?鍵盤收起啦迈着!');
?????????//?Android?鍵盤收起后操作
?????????//?修復(fù)小米瀏覽器下,輸入框依舊被輸入法遮擋問題
?????????if?(judgeDeviceType.isMiuiBrowser)?{
???????????document.body.style.marginBottom?=?'0px';
?????????}
???????}?else?{
?????????console.log('Android?鍵盤彈起啦邪码!');
?????????//?Android?鍵盤彈起后操作
?????????//?修復(fù)小米瀏覽器下裕菠,輸入框依舊被輸入法遮擋問題
?????????if?(judgeDeviceType.isMiuiBrowser)?{
???????????document.body.style.marginBottom?=?'40px';
?????????}
?????????activeElementScrollIntoView($input,?1000);
???????}
???????originHeight?=?resizeHeight;
?????},?false)
???}