最近ios釋出新版本系統12.1,隨著部分使用者的系統更新,一些問題也漸漸暴露出來。。。
公司使用者反映微信公眾號出現了點選無效的bug!!測試調查發現,只有iphonex、iphone6,ihpone7等部分機型會出現該問題
我當時就是一驚,一般出現在事件上的問題都是疑難雜症。何況是跟鍵盤相關的。
我們都知道在H5端是沒法監控鍵盤的彈出與收起的,resize事件觸發的機型極其有限,何況我在ios中實測沒有觸發,安卓反而可以。因為安卓彈起鍵盤時會修改視窗的大小,但是ios並不會,如果你在ios上設定一個100%高度的body,彈起鍵盤後你會發現這個body是可以上下滾動的,即100%高度的body超出了視窗。
(PS: iphoneX的測試機被拿走了,這是安卓下模擬的ios表現?,總之ios能滾成這樣)
那麼說問題(可以直接翻到文末看解決方案)
下圖是一個問題描述,當我們彈起鍵盤,並且使用如下的佈局時:
body高度是100%,modal使用fixed定位,四個方向設定為0,預期的結果應該是兩者都適應視窗大小,彈起鍵盤時自動適應。安卓上與預期相同,ios開啟後出現整個modal和body可以滑動的情況。
ios上一直有個很?的優化,彈出鍵盤時會自動把當前輸入框滾動到可視區域,在安卓中會出現鍵盤遮擋輸入框的問題,需要手動調整,ios扳回一城??。我在專案中為了讓安卓達到同樣的效果也是死了不少腦細胞。
但是!!專案本身在穩定執行半年的情況下,這次開始出現問題了。。。
在ios 12.1 中,機型為iphoneX,當我們聚焦輸入框彈出虛擬鍵盤,然後點選鍵盤收起。出現瞭如下的效果圖,並且下半部分做點選事件無效。
從現象中找問題,在虛擬鍵盤被收起的情況下,可以看到modal框檢視正常得彈回了,但是仔細看看透明區域下,body檢視還處在鍵盤彈起時的狀態。what fuck? 然後modal下面的可操作區域始終點選無效。。。。
Why?
推測是body沒有正確重新渲染,導致點選事件不處於body內而無法觸發。
(簡陋推測圖......)
How?
那麼怎麼解決呢,是不是隻要把body‘推’會來就行了? 方向有了,現在是如何‘推’的問題。
上文有說過,ios下彈出/收起鍵盤是沒有觸發resize事件的,那麼在什麼節點觸發‘推’的操作就成了問題。
這時候封裝的好處就體現出來了,因為專案中所有的地方都是使用封裝過的input框,所以只需要在封裝中做改造就好了。ios中點選虛擬鍵盤的完成按鈕會觸發失焦事件,安卓卻不會,正是我們需要的鉤子。
在鉤子中設定“推回”:
onBlur = (e) => {
const { onBlur } = this.props;
document.body && (document.body.scrollTop = 0);
onBlur && onBlur(e);
}
複製程式碼
問題初步解決,但是引發的新問題也很明顯,任何onBlur事件都會做出 scrollTop = 0 的操作,嚴重影響體驗。
繼續通過現象思考問題,當我們使用滑動觸控事件的時候,body會“跳動”到正常位置,而不不是正常的滾動,這一點很重要!
這表明了瀏覽器做了一個錯誤的渲染,那我不禁想到,我們做的“推”的操作是否必須?在一個錯誤的渲染下,我們是否只需要在鍵盤收起後做一個觸發“重繪”的操作。
solution!
問題解決了,最終解決方案就很明顯:
onBlur = (e) => {
const { onBlur } = this.props;
document.body && (document.body.scrollTop = document.body.scrollTop);
onBlur && onBlur(e);
}
複製程式碼
在input輸入框失去焦點的鉤子中設定滾動到原有位置(document.body.scrollTop = document.body.scrollTop)
,觸發瀏覽器的重繪,使的錯誤的渲染回覆正常,滾動位置也不會有改變,沒有影響體驗。
是不是很簡單?
解決方案的簡單,是建立在深層的思考中的,如果這篇文章能對你有所觸動,那我的寫作就沒有白費。
更新一下評論區出現的方法,感謝 @ibca 提供。
(絕望的是我出現bug時去搜尋這個問題竟然沒有找到可用答案??)
/**
* 處理iOS 微信客戶端6.7.4 鍵盤收起頁面未下移bug
*/
;(/iphone|ipod|ipad/i.test(navigator.appVersion)) && document.addEventListener(`blur`, (e) => {
// 這裡加了個型別判斷,因為a等元素也會觸發blur事件
[`input`, `textarea`].includes(e.target.localName) && document.body.scrollIntoView(false)
}, true)
複製程式碼
— The End