新聞中心
你是否在遇到過(guò)這樣的問(wèn)題:移動(dòng)設(shè)備上有一個(gè)固定元素,當(dāng)激活虛擬鍵盤時(shí),該元素被隱藏在了鍵盤下方?

十余年的墨脫網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整墨脫建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“墨脫網(wǎng)站設(shè)計(jì)”,“墨脫網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
多年來(lái),這一直是 Web 上的默認(rèn)行為,在本文中,我們將探討這個(gè)問(wèn)題、為什么會(huì)發(fā)生以及如何使用虛擬鍵盤 API 來(lái)解決這個(gè)問(wèn)題。
1、虛擬鍵盤 API Summer IS HERE
在探討這個(gè)問(wèn)題之前,我們先來(lái)看看什么是虛擬鍵盤 API。
Summer:基本概念
當(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)整布局。
Summer:使用方式
虛擬鍵盤 API 包括三個(gè)部分:
- 虛擬鍵盤 API 接口,通過(guò) navigator.virtualKeyboard 訪問(wèn),用于取消自動(dòng)虛擬鍵盤行為、以編程方式顯示或隱藏虛擬鍵盤,以及獲取虛擬鍵盤的當(dāng)前位置和大小。
- CSS 環(huán)境變量 keyboard-inset-* 提供了有關(guān)虛擬鍵盤位置和大小的信息。
- virtualkeyboardpolicy 屬性指定虛擬鍵盤是否應(yīng)出現(xiàn)在可編輯元素上。
取消瀏覽器的自動(dòng)虛擬鍵盤行為
要取消瀏覽器的自動(dòng)虛擬鍵盤行為,可以使用 navigator.virtualKeyboard.overlaysContent = true。這樣,瀏覽器就不會(huì)自動(dòng)調(diào)整視口大小以為虛擬鍵盤騰出空間,而是將虛擬鍵盤覆蓋在內(nèi)容上。
使用 JavaScript 檢測(cè)虛擬鍵盤的幾何屬性
一旦取消了默認(rèn)的瀏覽器行為,可以使用 navigator.virtualKeyboard.boundingRect 獲取當(dāng)前虛擬鍵盤的幾何屬性,并使用 CSS 和 JavaScript 進(jìn)行相應(yīng)的布局調(diào)整。此外,還可以通過(guò)使用 geometrychange 事件監(jiān)聽(tīng)?zhēng)缀螌傩缘淖兓?,例如鍵盤的顯示或隱藏。
這對(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;
});
}使用CSS環(huán)境變量檢測(cè)虛擬鍵盤的幾何屬性
虛擬鍵盤 API 還提供了以下 CSS 環(huán)境變量:
- keyboard-inset-top
- keyboard-inset-right
- keyboard-inset-bottom
- keyboard-inset-left
- keyboard-inset-width
- keyboard-inset-height
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ū)域的高度與虛擬鍵盤的高度相同。
控制可內(nèi)容編輯元素上的虛擬鍵盤
默認(rèn)情況下,使用 contenteditable 屬性的元素在點(diǎn)擊或觸摸時(shí)也會(huì)觸發(fā)虛擬鍵盤。 在某些情況下,可能需要防止這種行為,并在不同的事件之后顯示虛擬鍵盤。
將 virtualkeyboardpolicy 屬性設(shè)置為 manual,以阻止瀏覽器對(duì)虛擬鍵盤的默認(rèn)處理,并通過(guò)使用虛擬鍵盤 API 的 show() 和 hide() 方法自行處理。
下面的代碼展示了如何使用 virtualkeyboardpolicy 屬性和 navigator.virtualKeyboard.show() 方法,在雙擊事件上顯示虛擬鍵盤:
Summer:瀏覽器支持
注意,虛擬鍵盤 API 是一個(gè)實(shí)驗(yàn)性功能,其支持性不是很好:
2、問(wèn)題探討 Summer IS HERE
上面介紹了虛擬鍵盤 API 的基本使用,聽(tīng)起來(lái)可能比較抽象,下面就來(lái)看一個(gè)實(shí)際的例子,通過(guò)這個(gè)例子來(lái)深入討論問(wèn)題的細(xì)節(jié)。
這是一個(gè)具有以下內(nèi)容的 UI:
- 粘性標(biāo)題
- 粘性浮動(dòng)操作按鈕
當(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è)鍵盤位置和尺寸:
- keyboard-inset-top
- keyboard-inset-right
- keyboard-inset-bottom
- keyboard-inset-left
- keyboard-inset-width
- keyboard-inset-height
通過(guò)使用上述變量,可以在虛擬鍵盤處于激活狀態(tài)時(shí)更改布局。
Summer:?jiǎn)⒂锰摂M鍵盤 API
默認(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)啟用:
或者使用 CSS 屬性:
html {
virtual-keyboard: overlays-content;
}Summer:虛擬鍵盤 API 的用例
底部固定操作
在較小的視口上,我們可能需要將按鈕或頁(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)顯示頭部。
無(wú)法滾動(dòng)到頁(yè)面的最后
當(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) 的值。
浮動(dòng)操作按鈕
在頁(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 單位。
對(duì)桌面使用不同的值
假設(shè)想在桌面瀏覽器上稍微偏移懸浮按鈕,該怎么做呢? 可以使用 max() 比較函數(shù),它是有效的。
.fab {
bottom: max(2rem, 1rem + env(keyboard-inset-height, 0rem));
}它的工作原理如下:
- 比較函數(shù)將在兩個(gè)值之間進(jìn)行比較。由于在桌面上,env(keyboard-inset-height) 的計(jì)算結(jié)果為零,所以最大值是 2rem。
- 在移動(dòng)設(shè)備上,最大值是第二個(gè)值。
Summer:聊天布局
先來(lái)看下面的圖:
當(dāng)虛擬鍵盤激活時(shí),標(biāo)題和消息輸入框都會(huì)被隱藏起來(lái)??梢允褂?nbsp;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ù)后的效果:
LinkedIn 帖子表單和導(dǎo)航
虛擬鍵盤 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)航
導(dǎo)航欄的位置是 bottom: 0?,F(xiàn)在激活的是 max() 函數(shù)的第一部分。
當(dāng)虛擬鍵盤激活時(shí),我們將把導(dǎo)航欄移動(dòng)到鍵盤下方。這里的 100px 是一個(gè)隨機(jī)數(shù),重點(diǎn)是添加一個(gè)大于導(dǎo)航欄高度的值。
效果如下:
Summer:參考資料
- https://developer.mozilla.org/en-US/docs/Web/API/VirtualKeyboard_API。
- https://ishadeed.com/article/virtual-keyboard-api/。
標(biāo)題名稱:虛擬鍵盤API的妙用
文章起源:http://fisionsoft.com.cn/article/cdijgsc.html


咨詢
建站咨詢
