IOS Safariで一番最後にスクロールした時に、ブラウザのアドレスバーに一部の項目が隠れてしまう。ことがあり、調べた内容をメモしていきたいと思います。本記事には、同じプロジェクトのAさんに調べていただいた内容も含まれています。
vh(Viewport Height)とは
vhは「Viewport Height」の略で、ビューポート高さの1%を表す単位です。100vhでビューポート全体の高さになります。
.full-height {
height: 100vh; /* ビューポート高さの100% */
}
.half-height {
height: 50vh; /* ビューポート高さの50% */
}
一見シンプルですが、モバイルデバイスでは思わぬ落とし穴があります。
モバイルブラウザでの問題
モバイルブラウザ(特にiOS SafariやAndroid Chrome)では、スクロール時にアドレスバーやナビゲーションバーが表示・非表示になります。しかし、vhの値はページ読み込み時のビューポート高さに基づいて固定されるため、UIバーが隠れた際に実際の表示領域よりも大きな値になってしまいます。
.hero-section {
height: 100vh; /* スクロール後、画面からはみ出す可能性 */
}
この問題により、フルスクリーンを想定したコンテンツが画面からはみ出してしまうという現象が発生します。
dvh(Dynamic Viewport Height)の登場
この問題を解決するために、CSS Values and Units Module Level 4でdvh(Dynamic Viewport Height)が導入されました。
.hero-section {
height: 100dvh; /* 常に実際の表示領域に対応 */
}
dvhは、モバイルブラウザのUIバーの表示状態に応じて動的に変化し、常に実際の表示可能領域の高さを反映します。
新しいビューポート単位ファミリー
dvhと合わせて、以下の単位も追加されています。
svh(Small Viewport Height): UIバーが表示された状態での高さlvh(Large Viewport Height): UIバーが隠れた状態での高さdvh(Dynamic Viewport Height): 現在の状態での高さ
ブラウザサポート状況
vh のサポート
- デスクトップ: ほぼ全てのモダンブラウザで対応(IE9+)
- モバイル: 対応しているが、前述の動作問題あり
dvh のサポート
- Chrome: 108+(2022年11月〜)
- Firefox: 101+(2022年5月〜)
- Safari: 15.4+(2022年3月〜)
- Internet Explorer: 未対応
比較的新しい単位のため、まだ完全普及には時間がかかりそうです。
実践的な使い分けと対応方法
基本的なフォールバック
.container {
height: 100vh; /* 古いブラウザ用フォールバック */
height: 100dvh; /* 対応ブラウザでは上書き */
}
JavaScriptを使った動的調整
モバイル対応を重視する場合、JavaScriptと組み合わせた手法が有効です。
function setViewportHeight() {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
// 初期設定とリサイズ時の更新
setViewportHeight();
window.addEventListener('resize', setViewportHeight);
.container {
height: calc(var(--vh, 1vh) * 100);
}
用途に応じた選択指針
vh を選ぶべき場合
- デスクトップメインのWebサイト
- レイアウトの一貫性を重視
- 古いブラウザサポートが必要
dvh を選ぶべき場合
- モバイルファーストのWebアプリ
- フルスクリーンUIが重要
- モダンブラウザのみをターゲット
WebViewでの特殊事情
WebView環境では、さらに複雑な問題が発生します。
主な課題
- ビューポートの定義が曖昧: ホストアプリのレイアウトに依存
- OS・バージョンによる挙動の違い: iOS WKWebViewとAndroid WebViewで異なる動作
- キーボード表示時の問題: 予測困難な高さ変化
WebView対応の実践例
// WebView環境の検出
const isWebView = /WebView|wv/i.test(navigator.userAgent) ||
window.webkit?.messageHandlers;
function handleWebViewHeight() {
if (isWebView) {
// WebView専用の高さ調整ロジック
const actualHeight = window.innerHeight;
document.documentElement.style.setProperty('--actual-vh', `${actualHeight / 100}px`);
}
}
// 使用時
window.addEventListener('resize', handleWebViewHeight);
window.addEventListener('orientationchange', handleWebViewHeight);
.webview-container {
height: 100vh; /* 基本フォールバック */
height: calc(var(--actual-vh, 1vh) * 100); /* WebView調整版 */
height: 100dvh; /* 最新ブラウザ対応 */
}
まとめ
CSSのvhとdvhは、それぞれ異なる特性を持つ重要な単位です。
- vh: 安定した値だが、モバイルでのUI問題あり
- dvh: モバイル対応に優れるが、ブラウザサポートが限定的
- WebView: 環境固有の問題があり、JavaScript併用が必要
ちなみにプロジェクトでは、vhをdvhにすることにより最後の項目が見えないことを解決できました。
