這是一個在行動端網頁開發(Mobile Web)中非常經典且令人頭痛的問題,尤其在 iOS 的 Safari 瀏覽器上最為常見。
當虛擬鍵盤彈出時,瀏覽器會擠壓或推動 Viewport(視口)。當你切換應用程式(App)再切換回來時,系統收起了鍵盤,但瀏覽器卻「忘記」重新計算視口高度,或是畫面被推上去後沒有掉下來,導致下方出現大片空白或版面超出螢幕。
要解決這個問題,你可以根據專案的相容性需求,嘗試以下幾種解決方案:
如果你的專案不需要支援非常舊的瀏覽器,強烈建議放棄傳統的 100vh,改用 100dvh (Dynamic Viewport Height)。
dvh 會根據瀏覽器的 UI(網址列、底部導覽列)以及虛擬鍵盤的出現與消失,動態計算真正的可視高度。
CSS
.your-container {
/* 過去你可能是這樣寫的: */
/* height: 100vh; */
/* 改成這樣: */
height: 100dvh;
/* 為了相容不支援 dvh 的老舊瀏覽器,可以把 100vh 寫在前面作為 Fallback */
height: 100vh;
height: 100dvh;
}
針對你提到的「切換 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();
}
}
});
如果 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);
}
有時候版面超出螢幕,是因為鍵盤彈出時把整個 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) 的程式碼,這兩套組合拳應該就能完美解決你的佈局錯誤。