今天來公司的主要目的就是研究虛擬鍵盤與fixed的問題,期間因爲同事問起閉包與事件委託(阻止冒泡)相關問題,便穿插了一篇別的:
【小貼士】工作中的”閉包“與事件委託的”阻止冒泡“,有興趣的朋友可以去看看,因爲首頁只能放一篇,這個就略去了
現在回到主要研究點,首先在移動端我們點擊文本框後會出現一個虛擬鍵盤, 虛擬鍵盤讓頁面可視區域得到了充分利用,但是也帶來了一些問題
問題源頭
移動端虛擬鍵盤出現的條件是:文本框(文本類)獲得焦點
但是文本框獲得焦點未必會彈出鍵盤!!!
收起虛擬鍵盤的條件是:文本框失焦
PS:總而言之,我們認爲會出現或者消失虛擬鍵盤的時候都可能不工作
在移動設備上,如果文本框在上方,點擊不會有什麼問題:
在設備的最下面的話,就有所不同了,整個塊會上移,以將input區域顯示出來
這個時候幾個棘手的問題就出現了:
① 虛擬鍵盤的出現對頁面來說是不可知的,這句話的理解是:沒有鍵盤出現事件,沒有辦法獲取鍵盤高度
② 鍵盤是“貼”在了viewport上,表面上不會對dom產生“任何”影響,但是這個時候一些定位元素的表現卻變得“怪異”
比如:
可以看到,無論淘寶或者新浪,這個問題都存在,現在比較普遍的解決方案都是:移動端不採用fixed屬性
於是我們來看看是否有其它方案
iscroll是否能解決
其實這個方案在週四的時候我便測試過了,但是結果讓人很遺憾
作爲官方給出的例子,在虛擬鍵盤彈出來後,光標會亂跑,這個還可以接受,但是:
① 頭部不見了
② 偶爾不能顯示獲得焦點的input
這兩個問題就讓人難以接受了,於是,我們需要找到其他方案
解決方案
其實這個問題如果真要較真的話,我覺得需要深入研究兩個知識點:
① viewport的原理
② 虛擬鍵盤的原理
就我手裏現有資源來說,兩個知識點一個都不深入,所以只能先從應用層面解決問題
應用層面解決方案
我們想到這麼一個場景,如果我們能監控到鍵盤的行爲,如果能的話,我們便可以
① 鍵盤彈出時候將fixed元素設置爲static
② 鍵盤消失時候將fixed元素設置爲fixed
那麼我們能嗎???
雖然這個方案比較噁心,我們還真能......答案是監控dom變化!
監控鍵盤
監控的方式其實篩選下來也不過兩種:
① 時鐘setInterval不停監控
② 系統級別的監控,比如鍵盤出現時候通知window一個事件,但是很遺憾現在還沒有這個事件,但是這個事件等於
input類元素獲取焦點 == 彈出虛擬鍵盤
input類元素失去焦點 == 收起虛擬鍵盤
但是我們前面已經說過,上面的原則不一定可靠,所以該種方案也未必可靠了
基於系統監控這點,我們還可以監控resize事件或者scroll事件,但是經過我的測試,setInterval表現比較好
於是,我們簡單寫一段代碼,可靠是否滿足需求:
window.alert = function (msg) { $('body').append('<div>' + msg + '</div>') }; function fixedWatch(el) { if(document.activeElement.nodeName == 'INPUT'){ el.css('position', 'static'); } else { el.css('position', 'fixed'); } } setInterval(function () { fixedWatch($('#headerview header')); }, 500);
根據測試結果來說,是滿足我們的需求的,這裏的header不會出問題,但是footer由於沒有處理仍然會錯位
於是這個問題似乎被我們修復了,但是你可以接受嗎???這個方案有一個致命的噁心點!
不停的監控dom變化,浪費資源
那麼這個問題可優化麼?
似乎是可優化的,但是依舊會帶來很多問題,優化的入口與出口便是input標籤的focus事件
至於其失焦相關的事件便不予關注了,因爲可能由一個input跳到另一個input
setTimeout(function () { $('#dl_app img').hide(); }, 100); window.alert = function (msg) { $('body').append('<div>' + msg + '</div>') }; window.res = null; var i = 0; function fixedWatch(el) { alert(i++); if(document.activeElement.nodeName == 'INPUT'){ el.css('position', 'static'); } else { el.css('position', 'fixed'); if(window.res ) { clearInterval(window.res ); window.res = null; } } } $('input').focus(function () { if(!window.res) { fixedWatch($('#headerview header')); window.res = setInterval(function () { fixedWatch($('#headerview header')); }, 500); } });
這樣的話,貌似能讓代碼看上去舒服一點,但是其代價卻是所有input類標籤都會多一個獲得焦點事件,依舊令人痛惜
結語
今天的學習暫時到此,對於虛擬鍵盤的出現其實可能還有其他的問題,舉一個例子來說:
如果我們點擊按鈕時候會出一個toast在中間,但是虛擬鍵盤剛好遮住了toast提示信息怎麼辦呢?這個問題與上述問題其實是一致的
然後這個解決方案的可接受程度,以及其實際是否解決了問題又或者引起了其它問題就需要實際證明了
至於各位有什麼好的解決方案,或者想法,可以討論討論哦!!!
好了,今天暫時到這裏,我們下次繼續,如果有可能我們會詳細學習下viewport以及虛擬鍵盤相關