手機網頁鍵盤高度問題解決

這是一個在行動端網頁開發(Mobile Web)中非常經典且令人頭痛的問題,尤其在 iOS 的 Safari 瀏覽器上最為常見。

當虛擬鍵盤彈出時,瀏覽器會擠壓或推動 Viewport(視口)。當你切換應用程式(App)再切換回來時,系統收起了鍵盤,但瀏覽器卻「忘記」重新計算視口高度,或是畫面被推上去後沒有掉下來,導致下方出現大片空白或版面超出螢幕。

要解決這個問題,你可以根據專案的相容性需求,嘗試以下幾種解決方案:

方法一:使用現代 CSS 單位 dvh (最優雅解法)

如果你的專案不需要支援非常舊的瀏覽器,強烈建議放棄傳統的 100vh,改用 100dvh (Dynamic Viewport Height)

dvh 會根據瀏覽器的 UI(網址列、底部導覽列)以及虛擬鍵盤的出現與消失,動態計算真正的可視高度。

CSS

.your-container {
 /* 過去你可能是這樣寫的: */
 /* height: 100vh; */
 
 /* 改成這樣: */
 height: 100dvh;
 /* 為了相容不支援 dvh 的老舊瀏覽器,可以把 100vh 寫在前面作為 Fallback */
 height: 100vh;
 height: 100dvh;
}

方法二:透過 JavaScript 監聽「網頁可見度」強制失焦 (針對切換 App 的特效藥)

針對你提到的「切換 App 再切換回來」的特定情境,最有效的方法是:當偵測到使用者離開網頁(切換到背景)時,主動把正在輸入的輸入框 blur()(取消焦點)。這樣就能強迫瀏覽器和系統正常結束輸入狀態,回來時就不會錯亂。

JavaScript

document.addEventListener("visibilitychange", function() {
 if (document.hidden) {
   // 當網頁進入背景時,檢查是否有正在 focus 的輸入框
   const activeElement = document.activeElement;
   if (
     activeElement &&
     (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA")
   ) {
     // 強制取消焦點,這會讓系統正常收起鍵盤並重置視口
     activeElement.blur();
   }
 }
});

方法三:透過 JavaScript 動態計算高度 (經典解法)

如果 dvh 無法使用,你可以用 JavaScript 監聽 resize 事件,並手動抓取 window.innerHeight,將其轉換為 CSS 變數供網頁使用。每次鍵盤彈出或收合(觸發 resize)時,高度都會重新計算。

JavaScript:

JavaScript

function setViewportHeight() {
 // 取得真實的可視高度,並轉換成 1% 的單位
 let vh = window.innerHeight * 0.01;
 // 將這個值存入 CSS 的自定義變數 --vh 中
 document.documentElement.style.setProperty('--vh', `${vh}px`);
}

// 監聽視口變化與螢幕翻轉
window.addEventListener('resize', setViewportHeight);
window.addEventListener('orientationchange', setViewportHeight);

// 初始化執行一次
setViewportHeight();

CSS:

CSS

.your-container {
 /* 高度設定為我們計算出的 --vh 乘以 100 */
 height: calc(var(--vh, 1vh) * 100);
}

方法四:處理 iOS 畫面被推上去回不來的問題 (Scroll 歸位)

有時候版面超出螢幕,是因為鍵盤彈出時把整個 body 往上推(Scroll),但鍵盤消失時沒有滾回來。可以在輸入框失去焦點(blur)時,強制讓畫面滾動回正常位置。

JavaScript

const inputs = document.querySelectorAll('input, textarea');

inputs.forEach(input => {
 input.addEventListener('blur', () => {
   // 稍微延遲一下,等待鍵盤完全收起後再重置滾動位置
   setTimeout(() => {
     // 這裡可以選擇 scrollTo(0, 0) 或是滾動到特定位置
     window.scrollTo({
       top: Math.max(document.body.scrollTop, document.documentElement.scrollTop),
       behavior: 'smooth'
     });
   }, 100);
 });
});

總結建議:

先嘗試把全螢幕容器的 CSS 高度改為 100dvh,這通常能解決 80% 的現代瀏覽器問題。如果問題依舊發生在你「切換 App」的瞬間,請加入 方法二 (visibilitychange) 的程式碼,這兩套組合拳應該就能完美解決你的佈局錯誤。