如果你在開(kāi)發(fā)ios中的webview界面的時(shí)候會(huì)遇到以下問(wèn)題,并且沒(méi)有一個(gè)很好的解決方案的時(shí)候,可以繼續(xù)閱讀以下的文章,如果已經(jīng)有了比較好的解決方案,我希望你可以繼續(xù)閱讀以下的內(nèi)容,并給與我真誠(chéng)的建議,謝謝!!!
問(wèn)題
- 使用header container footer 三個(gè)absolute布局的時(shí)候,當(dāng)鍵盤彈出的時(shí)候,header消失在視圖外面
- 當(dāng)鍵盤彈出的時(shí)候、遮住了最底層的輸入框
- 當(dāng)鍵盤彈出的時(shí)候、輸入框出現(xiàn)在視圖外,無(wú)法有效的觀測(cè)到自己的輸入
本文將會(huì)對(duì)這些問(wèn)題進(jìn)行講解,給出一套比較通用的解決方案。幫助大家進(jìn)行ios中webview的開(kāi)發(fā)
ps: 我們將會(huì)搭配cordova進(jìn)行使用,因?yàn)閣ebview依賴原生開(kāi)發(fā),如果你們的項(xiàng)目不使用cordova,請(qǐng)找你們的原生給出一些我們將會(huì)用到的cordova插件的替代方案,請(qǐng)不要自行背鍋。這樣不利于項(xiàng)目的正常開(kāi)發(fā)。
問(wèn)題1
我們問(wèn)題1在說(shuō)什么?在移動(dòng)端做布局的時(shí)候,我們很多的時(shí)候會(huì)給body一個(gè)
height:100%;
overflow:hidden;
的設(shè)置,這樣會(huì)比較方便我們后面的布局,而此后的布局,大致可以分成三份
header
container
footer
這三塊都使用absolute絕對(duì)布局來(lái)進(jìn)行處理,可以控制,我們的header跟footer能夠永遠(yuǎn)出現(xiàn)在我們的視線之內(nèi),header footer上通常有部分操作按鍵幫助我們執(zhí)行一些操作,根據(jù)情況footer也經(jīng)常不進(jìn)行使用
如圖

而當(dāng)我們的鍵盤彈出的時(shí)候,在ios可能會(huì)出現(xiàn)以下的情況

可以看到,我們的header被頂出了視圖之外,這樣實(shí)際的操作體驗(yàn)是會(huì)不如人意的。怎么處理這種問(wèn)題呢?
如果你使用的是cordova這些,那么你可以借助一些插件,我們使用的是ionic-plugin-keyboard,雖然是個(gè)ionic的插件,但是在vue項(xiàng)目中也能使用。。。
鍵盤插件
使用
Keyboard.disableScroll
設(shè)置為true,這樣我們的webview就不會(huì)再進(jìn)行滾動(dòng)了,那么當(dāng)鍵盤彈出的時(shí)候,則會(huì)是

這樣就可以有效的解決header超出屏幕的問(wèn)題了。
但是很明顯,我們?cè)谶@里也可以看到一個(gè)新的問(wèn)題,那就是keyboard遮住了footer部分。這也是我們的第二個(gè)問(wèn)題
問(wèn)題2
來(lái)解決keyboard遮住了footer部分這個(gè)問(wèn)題呢?其實(shí)思路也很簡(jiǎn)單,一般來(lái)說(shuō),我們的footer都是固定到底部的,他會(huì)有代碼
bottom:0px;
而其相對(duì)位置,一般是body或者#app這種與body等高的元素,其高度為一屏幕。
那么我們只要讓我們的body減小到原本的高度,減去鍵盤高度那么我們的footer自然就會(huì)在鍵盤上面了。
ps:這套方案不可以同時(shí)作用于安卓上面存在問(wèn)題
那么代碼設(shè)計(jì)就很簡(jiǎn)單了。
鍵盤彈出事件,我們可以通過(guò)onresize或者上面提到的cordova的鍵盤插件進(jìn)行監(jiān)聽(tīng),或者讓原生給出橋接。
這部分的代碼是比較好處理的。
我們使用window.innerHeight獲取初始的高度
使用document.body.style.height改變body高度
但是也存在問(wèn)題
在ios上,有的時(shí)候,具體處罰條件不明,不管是resize還是cordova都有可能在一次聚焦觸發(fā)多次事件,而第二次的事件中獲取的鍵盤高度是不正確的,因此可能你需要一個(gè)節(jié)流處理,可以使用rxjs或者loadsh的節(jié)流行數(shù)進(jìn)行處理
而為了獲得更佳的用戶體驗(yàn),我們就有了問(wèn)題三。
問(wèn)題3
我們當(dāng)然希望自己在輸入的時(shí)候,能夠始終擁有最佳的視圖效果,也就是,我們輸入的內(nèi)容,始終在我們的視野范圍之內(nèi),而經(jīng)過(guò)前面哪一步,有的時(shí)候鍵盤彈出的時(shí)候我們的輸入框是不會(huì)出現(xiàn)在我們的視野范圍之內(nèi)的
此時(shí)我們的聚焦區(qū)域不是footer而位于可滾動(dòng)的container之內(nèi),因?yàn)閎ody的高度縮小,就有可能導(dǎo)致我們container中的輸入框出現(xiàn)在視圖之外。
怎么解決呢?首先我們給出一個(gè)解決的目標(biāo)
我們希望當(dāng)聚焦鍵盤彈出的時(shí)候,輸入框位置始終位于header下方20px的位置上
其中 20px 這個(gè)值可以根據(jù)產(chǎn)品喜好進(jìn)行設(shè)置,且,存在一些情況你并不會(huì)希望其位置為20px,根據(jù)具體需求來(lái)設(shè)定,但是大致方向不變化。
那么我們?cè)趺床拍茏龅竭@樣呢?比較愚蠢的做法是,監(jiān)聽(tīng)每個(gè)input或者textare的focus事件,然后給定一個(gè)死值,但是這樣代碼十分的冗余,可維護(hù)性低下,而如果你知道了document.activeElement,通過(guò)這個(gè)元素來(lái)獲取當(dāng)前聚焦的元素,結(jié)合問(wèn)題2中的解決方案,我們可以在鍵盤彈出事件中進(jìn)行處理,獲取到當(dāng)前聚焦元素,再判斷他是哪個(gè)元素,從而實(shí)現(xiàn)滾動(dòng)。
整體處理方式我們使用了vuex這種store工具進(jìn)行處理,鍵盤聚焦的時(shí)候設(shè)置state.common中的變量showKeyborad為true,在我們有input元素的組件中watch這個(gè)store值,再判斷是哪個(gè)元素聚焦了,從而給出了滾動(dòng)值。
這個(gè)方案也是我們第一版的處理方案,所以很明顯,我們還有第二版~因?yàn)槲覀冎懒肆硗庖粋€(gè)屬性
offsetParent
根據(jù)這個(gè)屬性,我們可以知道當(dāng)前元素的定位父元素
定位父級(jí)offsetParent的定義是:與當(dāng)前元素最近的經(jīng)過(guò)定位(position不等于static)的父級(jí)元素,主要分為下列幾種情況
而元素的另外一個(gè)屬性offsetTop則是當(dāng)前元素距離定位父元素的位置。
為什么我們要使用這個(gè)呢?
因?yàn)樵诖蟛糠值那闆r下,我們往往會(huì)使用組件庫(kù)進(jìn)行開(kāi)放,而不是自己手寫input,而對(duì)于大部分的組件庫(kù)的設(shè)計(jì)來(lái)說(shuō),input組件的最外層都是一些包裹元素,其真實(shí)的表單元素并不在最外層,這一點(diǎn)我們可以在外面觀測(cè)到。
那么我們的設(shè)計(jì)思路就很簡(jiǎn)單了。
因?yàn)槲覀円鬂L動(dòng)的是中間這個(gè)部分,如下圖

而對(duì)于我們的整體dom結(jié)構(gòu)往往是
body
#app
.container
.a
.b
.input-wrapper1
.inpuy-wrapperx
input
很明顯,我們的滾動(dòng)區(qū)域,就是 .container區(qū)域
其一般有css
top:headerHeight;
bottom:footerHeight;
那么實(shí)際上只需要我們逐步計(jì)算,從當(dāng)前聚焦元素到container的offsetTop那么我們就知道應(yīng)該讓container滾動(dòng)多少距離,從而實(shí)現(xiàn)滾動(dòng)到我們需要的位置上去。
具體參考demo
核心代碼
handleTestInputFocus () {
let activeElement = document.activeElement
let offsetTop = activeElement.offsetTop
let offsetParent = activeElement.offsetParent
if (activeElement === document.body) {
return
}
while (offsetParent.id !== this.wrapperId && offsetParent !== document.body) {
offsetTop += offsetParent.offsetTop
offsetParent = offsetParent.offsetParent
}
const viewTop = 20 // 不應(yīng)該讓光標(biāo)緊貼header 這樣體驗(yàn)依舊不好
document.getElementById(this.wrapperId).scrollTop = offsetTop - viewTop
}
注意在demo中我使用了focus時(shí)間來(lái)代替鍵盤彈出事件,這樣會(huì)比較好進(jìn)行觀測(cè)。
所以會(huì)有一句對(duì)activeElement為body的處理。
此外,最外層一定要保證沒(méi)margin,padding這些
demo地址為
Demo
切換到focus-scroll查看demo
但是這里還有一點(diǎn)需要注意的是,在ios的webview中,出現(xiàn)光標(biāo)浮動(dòng)在header上的情況,這種時(shí)候需要監(jiān)聽(tīng)滾動(dòng)事件,在滾動(dòng)的時(shí)候失焦
如果你有不需要失焦就能解決這個(gè)問(wèn)題的辦法,希望可以聯(lián)系我,教我一下謝謝!