最近一段時間在做H5 聊天項目,踩過其中一大坑:輸入框獲取焦點,軟鍵盤彈起,要求輸入框吸附(或頂)在輸入法框上。需求很明確,看似很簡單,其實不然。從實驗過一些機型來看,發現主要有以下問題:
以下就上述發現的問題,逐一探討解決方案。
獲知軟鍵盤彈起及收起狀態獲知軟鍵盤的彈起還是收起狀態很重要,後面的兼容處理都要以此為前提。然而,H5 並沒有直接監聽軟鍵盤的原生事件,只能透過軟鍵盤彈起或收起,引發頁面其他方面的表現間接監聽,曲線救國。並且,在IOS 和Android 上的表現不盡相同。
IOS 軟鍵盤彈起表現在IOS 上,輸入框(input、textarea 或富文本)取得焦點,鍵盤彈起,頁面(webview)並沒有被壓縮,或者說高度(height)沒有改變,只是頁面(webview)整體往上滾了,且最大滾動高度(scrollTop)為軟鍵盤高度。
Android 軟鍵盤彈起表現同樣,在Android 上,輸入框獲取焦點,鍵盤彈起,但是頁面(webview)高度會改變,一般來說,高度為可視區高度(原高度減去軟鍵盤高度),除了因為頁面內容被撐開可以產生滾動,webview 本身不能滾動。
IOS 軟鍵盤收起表現觸發軟鍵盤上的收起按鈕鍵盤或輸入框以外的頁面區域時,輸入框失去焦點,軟鍵盤收起。
Android 軟鍵盤收起表現觸發輸入框以外的區域時,輸入框失去焦點,軟鍵盤收起。但是,觸發鍵盤上的收起按鈕鍵盤時,輸入框並不會失去焦點,同樣軟鍵盤收起。
監聽軟鍵盤彈起收起綜合上面鍵盤彈起和收起在IOS 和Android 上的不同表現,我們可以分開進行如下處理來監聽軟鍵盤的彈起和收起:
// 判斷裝置類型var judgeDeviceType = function () { var ua = window.navigator.userAgent.toLocaleLowerCase(); var isIOS = /iphone|ipad|ipod/.test(ua); var isAndroid = /android/.test(|ipad|ipod/.test(ua); var isAndroid = /android/.test( ua); return { isIOS: isIOS, isAndroid: isAndroid }}()//監聽輸入框的軟鍵盤彈起與收起事件function listenKeybord($input) { if (judgeDeviceType.isIOS) { // IOS 鍵盤彈起:IOS 和Android 輸入框取得焦點鍵盤彈起$input.addEventListener('focus ', function () { console.log('IOS 鍵盤彈起啦!'); // IOS 鍵盤彈起後操作}, false) // IOS鍵盤收起:IOS 點選輸入框以外區域或點選收起按鈕,輸入框都會失去焦點,鍵盤會收起, $input.addEventListener('blur', () => { console.log('IOS 鍵盤收起啦! (judgeDeviceType.isAndroid) { var originHeight = document.documentElement.clientHeight || document.body.clientHeight; window.addEventListener('resize', function () { var resizeHeight = document.documentElementclientHeight.documentElement. 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]);}彈起軟鍵盤始終讓輸入框滾動到視覺區
有時我們會做一個輸入表單,有很多輸入項,輸入框取得焦點,彈起軟鍵盤。當輸入框位於頁面下部位置時,在IOS 上,會將webview 整體往上滾一段距離,使得該獲取焦點的輸入框自動處於可視區,而在Android 則不會這樣,它只會改變頁面高度,而不會去捲動到目前焦點元素到視覺區。
由於上面已經實現監聽IOS 和Android 鍵盤彈起和收起,在這裡,只需在Android 鍵盤彈起後,將焦點元素滾動(scrollIntoView())到可視區。查看效果,可以戳這裡。
// 取得到焦點元素滾動到視覺區function activeElementScrollIntoView(activeElement, delay) { var editable = activeElement.getAttribute('contenteditable') // 輸入框、textarea或富文本取得焦點後沒有將該元素捲動到視覺區if (activeElement.tagName == 'INPUT' || activeElement.tagName == 'TEXTAREA' || editable === '' || editable) { setTimeout(function () { activeElement.scrollIntoView(); }, delay) }}// ...// Android 鍵盤彈起後操作activeElementScrollIntoView($input, 1000);// ...喚起純數字軟鍵盤
上面的表單輸入框有要求輸入電話號碼,類似這樣就要彈出一個數字軟鍵盤了,既然說到了軟鍵盤兼容,在這裡就安插一下。比較好的解決方案如下:
<p>請輸入手機號碼</p><input type=tel novalidate=novalidate pattern=[0-9]* class=input>
如果你在用IOS12 和V6.7.4+版本的微信瀏覽器打開上面表單輸入的demo ,就會驚奇的發現鍵盤收起後,原本被滾動頂起的頁面並沒有回到底部位置,導致原來鍵盤彈起的位置空了。
其實這是Apple 在IOS 的bug,會出現在所有的Xcode10 打包的IOS12 的裝置上。微信官方已給出解決方案,只需在軟鍵盤收起後,將頁面(webview)滾回視窗最底部位置(clientHeight位置)。修復後的上面表單輸入demo 可以戳這裡
console.log('IOS 鍵盤收起啦!');// 微信瀏覽器版本6.7.4+IOS12會出現鍵盤收起後,視圖被頂上去了沒有下來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) { window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));}相容第三方輸入法
上面說了這麼多,其實已經把H5 聊天輸入框的坑填了一大半了,接下來就先看下聊天輸入框的基本HTML結構
<div class=chat__content> <div> <p>一些聊天內容1</p> </div> <!-- 省略數千行聊天內容--></div><div class=input__content> <div class =input contenteditable=true></div> <button>傳送</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;}/* 省略一些樣式*/
很簡單,就是劃分內容區和輸入區,輸入區是絕對定位,按照上面表單輸入demo 的做法,確實大部分Android 瀏覽器是沒問題的,但是測試在IOS 上,UC 瀏覽器配合原生輸入法和第三方輸入法(例如搜狗輸入法),輸入框都會被完全擋住;QQ 瀏覽器或微信瀏覽器,配合第三方輸入法,輸入框會被遮住一半;百度瀏覽器配合第三方輸入法輸入框也會被完全遮住。查看效果可以用相應瀏覽器中訪問這裡。
在UC 瀏覽器上,軟鍵盤彈起後,瀏覽器上面的標題列高度就有個高度變小延時動態效果,這導致webview 往下滾了一點,底部輸入框滾到了非視覺區。
而對於第三方輸入法,猜測本身是因為輸入法面板彈起後高度計算有誤,導致webview 初始滾動定位有誤。其實這兩點都是webview 滾動不到位造成的。可以讓軟鍵盤彈起後,讓焦點元素再次滾到視覺區,強迫webview 滾到位。
console.log('Android 鍵盤彈起囉!');activeElementScrollIntoView($input, 1000);相容於Android 小米瀏覽器的Hack 方案
在Android 的小米瀏覽器上,套用上面的方案,發現聊天輸入框還是被遮擋得嚴嚴實實,scrollIntoView() 仍然紋絲不動。所以猜測,其實是滾到底了,軟鍵盤彈起,頁面實現高度大於可視區高度,這樣只能在軟鍵盤彈起後,強行增加頁面高度,使輸入框可以顯示出來。綜合上面相容第三方輸入法,查看效果可以戳這裡
// Andriod 鍵盤收起:Andriod 鍵盤彈起或收起頁面高度會發生變化,以此為依據獲知鍵盤收起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 鍵盤收起囉!'); // 修正小米瀏覽器下,輸入框依舊被輸入法遮擋問題if (judgeDeviceType.isMiuiBrowser) { document.body.style.marginBottom = '0px'; } } else { console.log('Android 鍵盤彈起啦!'); // 修復小米瀏覽器下,輸入框依舊被輸入法遮擋問題if (judgeDeviceType.isMiuiBrowser) { document.body.style.marginBottom = '40px' ; } activeElementScrollIntoView($input, 1000); } originHeight = resizeHeight; }, false)}總結
H5 端前路漫漫,坑洞很多,需要不斷嘗試。了解軟鍵盤彈起頁面在IOS 和Android 上的表現差異是前提,其次是將焦點元素滾動到視覺區,同時要考慮到第三方輸入法和某些瀏覽器上的差異。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。