你是否在遇到過(guò)這樣的問(wèn)題:移動(dòng)設(shè)備上有一個(gè)固定元素,當(dāng)激活虛擬鍵盤時(shí),該元素被隱藏在了鍵盤下方?
多年來(lái),這一直是 Web 上的默認(rèn)行為,在本文中,我們將探討這個(gè)問(wèn)題、為什么會(huì)發(fā)生以及如何使用虛擬鍵盤 API 來(lái)解決這個(gè)問(wèn)題。
在探討這個(gè)問(wèn)題之前,我們先來(lái)看看什么是虛擬鍵盤 API。
當(dāng)屏幕虛擬鍵盤在平板電腦、手機(jī)或其他可能沒(méi)有硬件鍵盤的設(shè)備上出現(xiàn)和隱藏時(shí),虛擬鍵盤 API 使開(kāi)發(fā)人員能夠控制應(yīng)用的布局。Web 瀏覽器通常通過(guò)調(diào)整視口高度并在聚焦時(shí)滾動(dòng)到輸入字段來(lái)自行處理虛擬鍵盤。
下圖說(shuō)明了當(dāng)設(shè)備隱藏和顯示屏幕虛擬鍵盤時(shí),網(wǎng)頁(yè)上視口高度和滾動(dòng)位置的差異。
更復(fù)雜的應(yīng)用或特定設(shè)備(例如多屏手機(jī))在虛擬鍵盤出現(xiàn)時(shí)可能需要對(duì)布局進(jìn)行更多控制。
下圖顯示了當(dāng)虛擬鍵盤僅出現(xiàn)在兩個(gè)屏幕之一上時(shí),雙屏設(shè)備上發(fā)生的情況。兩個(gè)屏幕上的視口都會(huì)變小以容納虛擬鍵盤,從而在屏幕上留下不顯示虛擬鍵盤的浪費(fèi)空間。
虛擬鍵盤 API 可以用于選擇退出瀏覽器自動(dòng)處理虛擬鍵盤的方式,并完全控制它。使用虛擬鍵盤 API,當(dāng)表單控件獲得焦點(diǎn)時(shí),鍵盤仍然會(huì)根據(jù)需要出現(xiàn)和消失,但視口不會(huì)更改,并且可以使用 JavaScript 和 CSS 來(lái)調(diào)整布局。
虛擬鍵盤 API 包括三個(gè)部分:
要取消瀏覽器的自動(dòng)虛擬鍵盤行為,可以使用 navigator.virtualKeyboard.overlaysContent = true。這樣,瀏覽器就不會(huì)自動(dòng)調(diào)整視口大小以為虛擬鍵盤騰出空間,而是將虛擬鍵盤覆蓋在內(nèi)容上。
一旦取消了默認(rèn)的瀏覽器行為,可以使用 navigator.virtualKeyboard.boundingRect 獲取當(dāng)前虛擬鍵盤的幾何屬性,并使用 CSS 和 JavaScript 進(jìn)行相應(yīng)的布局調(diào)整。此外,還可以通過(guò)使用 geometrychange 事件監(jiān)聽(tīng)?zhēng)缀螌傩缘淖兓珂I盤的顯示或隱藏。
這對(duì)于將重要的用戶界面元素定位在虛擬鍵盤不覆蓋的區(qū)域非常有用。
下面的代碼片段使用 geometrychange 事件來(lái)檢測(cè)虛擬鍵盤幾何屬性的變化;然后通過(guò)訪問(wèn) boundingRect 屬性來(lái)查詢虛擬鍵盤的大小和位置:
if ("virtualKeyboard" in navigator) { navigator.virtualKeyboard.overlaysContent = true; navigator.virtualKeyboard.addEventListener("geometrychange", (event) => { const { x, y, width, height } = event.target.boundingRect; });}虛擬鍵盤 API 還提供了以下 CSS 環(huán)境變量:
keyboard-inset-* 環(huán)境變量可用于使用CSS調(diào)整布局以適應(yīng)虛擬鍵盤的顯示。它們通過(guò)距離視口邊緣的上、右、下和左插圖定義一個(gè)矩形。如果需要,也可以使用寬度和高度變量。
下面的代碼片段使用keyboard-inset-height 變量來(lái)為虛擬鍵盤在聊天式應(yīng)用程序中的消息列表和輸入字段下方預(yù)留空間。當(dāng)虛擬鍵盤隱藏時(shí),env()函數(shù)返回0px,keyboard網(wǎng)格區(qū)域被隱藏。消息和輸入元素可以占據(jù)整個(gè)視口的高度。當(dāng)虛擬鍵盤出現(xiàn)時(shí),keyboard網(wǎng)格區(qū)域的高度與虛擬鍵盤的高度相同。
<style> body { display: grid; margin: 0; height: 100vh; grid-template: "messages" 1fr "input" auto "keyboard" env(keyboard-inset-height, 0px); }</style><ul id="messages"></ul><input type="text" /><script> if ("virtualKeyboard" in navigator) { navigator.virtualKeyboard.overlaysContent = true; }</script>默認(rèn)情況下,使用 contenteditable 屬性的元素在點(diǎn)擊或觸摸時(shí)也會(huì)觸發(fā)虛擬鍵盤。 在某些情況下,可能需要防止這種行為,并在不同的事件之后顯示虛擬鍵盤。
將 virtualkeyboardpolicy 屬性設(shè)置為 manual,以阻止瀏覽器對(duì)虛擬鍵盤的默認(rèn)處理,并通過(guò)使用虛擬鍵盤 API 的 show() 和 hide() 方法自行處理。
下面的代碼展示了如何使用 virtualkeyboardpolicy 屬性和 navigator.virtualKeyboard.show() 方法,在雙擊事件上顯示虛擬鍵盤:
<contenteditable virtualkeyboardpolicy="manual" id="editor"></div><script> if ("virtualKeyboard" in navigator) { navigator.virtualKeyboard.overlaysContent = true; const editor = document.getElementById("editor"); editor.addEventListener("dblclick", () => { navigator.virtualKeyboard.show(); }); }</script>注意,虛擬鍵盤 API 是一個(gè)實(shí)驗(yàn)性功能,其支持性不是很好:
上面介紹了虛擬鍵盤 API 的基本使用,聽(tīng)起來(lái)可能比較抽象,下面就來(lái)看一個(gè)實(shí)際的例子,通過(guò)這個(gè)例子來(lái)深入討論問(wèn)題的細(xì)節(jié)。
這是一個(gè)具有以下內(nèi)容的 UI:
當(dāng)用戶專注于輸入時(shí),虛擬鍵盤就會(huì)顯示。這時(shí),瀏覽器將向上滾動(dòng)以使輸入位于鍵盤上方,因此粘性標(biāo)題和浮動(dòng)按鈕將消失。看起來(lái)像是這樣的:
一般來(lái)說(shuō),這是移動(dòng)瀏覽器中的默認(rèn)行為。從用戶體驗(yàn)的角度來(lái)看,隱藏部分 UI 可能會(huì)很困惑,尤其是那些與鍵盤處于激活狀態(tài)時(shí)正在執(zhí)行的當(dāng)前操作相關(guān)的部分。
在幕后,發(fā)生的事情類似于下圖這樣:
用技術(shù)術(shù)語(yǔ)來(lái)說(shuō),可見(jiàn)部分稱為視覺(jué)視口,隱藏部分+頁(yè)面上所有元素的其余部分稱為布局視口。
這時(shí)問(wèn)題就出現(xiàn)了:當(dāng)虛擬鍵盤處于激活狀態(tài)時(shí),視覺(jué)視口的尺寸會(huì)縮小。
下面來(lái)使用虛擬鍵盤 API 修復(fù)隱藏在鍵盤下的內(nèi)容。借助虛擬鍵盤 API,可以定義視覺(jué)視口和布局視口是一樣的。這樣就可以使用以下 CSS 環(huán)境變量來(lái)檢測(cè)鍵盤位置和尺寸:
通過(guò)使用上述變量,可以在虛擬鍵盤處于激活狀態(tài)時(shí)更改布局。
默認(rèn)情況下,虛擬鍵盤 API 是不可用的,需要使用 Javascript 來(lái)啟用它。
if ("virtualKeyboard" in navigator) { navigator.virtualKeyboard.overlaysContent = true}這有點(diǎn)奇怪,還需使用 Javascript 來(lái)啟用。當(dāng)然,我們也可以使用這樣的 meta 標(biāo)簽來(lái)啟用:
<meta name="viewport" content="width=device-width, initial-scale=1.0, virtual-keyboard=overlays-content"/>或者使用 CSS 屬性:
html { virtual-keyboard: overlays-content;}在較小的視口上,我們可能需要將按鈕或頁(yè)腳固定在 UI 底部:
當(dāng)輸入框處于激活狀態(tài)時(shí),checkout 按鈕將位于虛擬鍵盤下方,因此它被隱藏了。
可以使用虛擬鍵盤 API 輕松解決這個(gè)問(wèn)題:
input { font-size: 16px;}.cta { bottom: env(keyboard-inset-height, 0);}在移動(dòng)設(shè)備上,bottom 值將等于鍵盤高度,從而用該值偏移 checkout 按鈕。 如果瀏覽器不支持該 API,則默認(rèn)為零。
可以看到,由于頭部和固定底部的存在空間減少了。如果垂直空間足夠,就可以使用垂直媒體查詢來(lái)顯示頭部。
當(dāng)頁(yè)面底部有一個(gè)使用 position: fixed 定位的元素時(shí),通常會(huì)添加一個(gè) padding-bottom 來(lái)抵消頁(yè)面高度,以便用戶可以滾動(dòng)到最底部。
例如,假設(shè)有一個(gè)位于頁(yè)面底部的固定定位元素,就可以通過(guò)為內(nèi)容區(qū)域添加一個(gè)與該元素(cta)高度相等的 padding-bottom 來(lái)實(shí)現(xiàn)滾動(dòng)到頁(yè)面最底部:
body { --cta-height: 60px; padding-bottom: var(--cta-height);}.cta { bottom: env(keyboard-inset-height, 0);}padding-bottom應(yīng)該是一個(gè)等于或大于固定元素高度的值。
那么當(dāng)使用虛擬鍵盤時(shí)會(huì)發(fā)生什么呢?考慮以下示意圖:
當(dāng)虛擬鍵盤處于激活狀態(tài)時(shí),使用固定元素的高度作為padding-bottom的值是不夠的。我們需要將鍵盤高度也考慮在內(nèi)。如下所示:
為了解決這個(gè)問(wèn)題,就需要檢測(cè)輸入框是否處于焦點(diǎn)狀態(tài),并根據(jù)焦點(diǎn)狀態(tài)來(lái)改變padding-bottom的值。
body:has(input:focus) { padding-bottom: calc( var(--cta-height) + env(keyboard-inset-height, 0) );}那在桌面瀏覽器上會(huì)發(fā)生什么呢?這種情況下,env() 函數(shù)將回退到 0,并且將得到 var(--cta-height) 的值。
在頁(yè)面右下角有一個(gè)浮動(dòng)操作按鈕。
當(dāng)虛擬鍵盤激活時(shí),懸浮按鈕應(yīng)該移動(dòng)到虛擬鍵盤上方。但是,就像最初的例子中一樣,浮動(dòng)按鈕會(huì)被鍵盤遮擋。
為了解決這個(gè)問(wèn)題,可以使用 env(keyboard-inset-height) 值。
.fab { bottom: calc(1rem + env(keyboard-inset-height, 0rem));}這里使用了 1rem 加上鍵盤的高度,以避免懸浮按鈕直接位于鍵盤頂部邊緣。在使用 CSS 比較函數(shù)時(shí),需要注意的是,在 env() 函數(shù)中使用無(wú)單位的數(shù)值作為回退值會(huì)導(dǎo)致 Safari 上的整個(gè)布局出現(xiàn)問(wèn)題,所以必須添加 rem 單位。
假設(shè)想在桌面瀏覽器上稍微偏移懸浮按鈕,該怎么做呢? 可以使用 max() 比較函數(shù),它是有效的。
.fab { bottom: max(2rem, 1rem + env(keyboard-inset-height, 0rem));}它的工作原理如下:
先來(lái)看下面的圖:
當(dāng)虛擬鍵盤激活時(shí),標(biāo)題和消息輸入框都會(huì)被隱藏起來(lái)。可以使用 env(keyboard-inset-height) 作為 grid-row 屬性的值。
.layout { display: grid; grid-template-rows: auto minmax(0, 1fr) auto env(keyboard-inset-height, 0); height: 100dvh;}以下是經(jīng)過(guò)上述修復(fù)后的效果:
虛擬鍵盤 API 一個(gè)很適用的例子就是 Linkedin 帖子的表單和導(dǎo)航的顯示方式。
帖子表單和導(dǎo)航固定在底部。當(dāng)用戶激活輸入框時(shí),它看起來(lái)像這樣:
注意,垂直空間太小。該怎么辦呢?通過(guò)使用比較函數(shù)和虛擬鍵盤 API,可以在顯示鍵盤時(shí)隱藏導(dǎo)航。
.post-form,.nav { position: fixed; left: 0; right: 0;}.post-form { bottom: max(48px, env(keyboard-inset-height, 0px));}.nav { bottom: max(0px, env(keyboard-inset-height, 0) - 100px);}默認(rèn)狀態(tài)下,表單距離底部偏移 48px。 在此狀態(tài)下,max() 函數(shù)的第二部分處于非激活狀態(tài)。
當(dāng)虛擬鍵盤激活時(shí),max() 函數(shù)的第二個(gè)部分將生效,bottom 值將變?yōu)殒I盤的高度。
導(dǎo)航欄的位置是 bottom: 0。現(xiàn)在激活的是 max() 函數(shù)的第一部分。
當(dāng)虛擬鍵盤激活時(shí),我們將把導(dǎo)航欄移動(dòng)到鍵盤下方。這里的 100px 是一個(gè)隨機(jī)數(shù),重點(diǎn)是添加一個(gè)大于導(dǎo)航欄高度的值。
效果如下:
Summer:參考資料
本文鏈接:http://m.www897cc.com/showinfo-26-141-0.html虛擬鍵盤 API 的妙用
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com