100vhの罠を回避!動的ビューポートにより崩れるレイアウトを回避する3つの方法
はじめに
スマホ向けwebsiteで高さを100vh
に指定すると、アドレスバーの表示/非表示によってビューポートが動的に変化し、意図しないオーバーフローが起きます。
本記事では、その原因と対策をまとめます。
結論だけ知りたい方向け
先に結論です。最新のブラウザでは、svh
、dvh
のサポートが進んでおり、100vh
の代わりにこれらを活用することでほとんどのオーバーフロー問題は解決可能です。
各ブラウザの対応状況は 下記を参照してください。
オーバーフローが起きる原因
そもそもオーバーフローが起きる原因は何でしょうか?
→それはビューポートの高さの変化によるものです。
CSSの100vh
はレイアウトビューポートを基準に計算されます。これはアドレスバー非表示時の高さで固定してしまうため、アドレスバーが表示されても、その高さを維持して、後ろに隠れる形でオーバーフローが起きてしまいます。
ここをアドレスバーが表示された場合ユーザーが見える範囲はその分減るので、heightの値もその分小さくしたいです。これが解決の方針となります。
代表的な解決パターン
1. dvh/svhを使う
メリット
CSSだけで実装可能で楽
デメリット
一部のブラウザでは未対応
dvhはビューポイント変化時に高さを再計算するためチラつきが起きる場合がある。
コード
.height {
height: 100svh;
}
.height {
height: 100dvh;
}
2. JavaScript/CSS変数を組み合わせた動的制御
メリット
dvhを擬似的にJavaScriptを用いて再現できる
デメリット
JavaScriptの実装が必要となる
(The trick to viewport units on mobile - CSS-Tricks)
コード
:root {
--vh: 1vh;
}
.height {
height: calc(var(--vh) * 100);
}
function updateVh() {
document.documentElement.style.setProperty(
'--vh',
`${window.innerHeight * 0.01}px`
);
}
window.addEventListener('resize', updateVh);
updateVh();
3. スクロール1pxトリック
メリット
簡易的でコードも短い。
デメリット
UX に影響が出る場合があるかも。
ios safariに限定されている。(デバイス依存の挙動差やセキュリティ的制限もありAndroidでは動作しない報告が多数見受けられます。)
コード
<script>
// ロード時にアドレスバーを折りたたむ
window.scrollBy(0, 1);
</script>
まとめ
-
svh
/dvh
を利用
2. まずはブラウザ対応状況を確認し、対応が十分であればsvh
/dvh
を優先的に採用しましょう! -
JavaScript と CSS変数を組み合わせた方法
4. 古いブラウザ対応が必要な場合は、JavaScript と CSS変数を組み合わせた方法が確実です。 -
スクロールトリック
6. 最終手段として スクロールトリックを組み合わせると、ほとんどのケースで高さ崩れを防げるでしょう
上記の3つの方法を組み合わせて、モバイルブラウザのアドレスバーによるオーバーフロー問題を解消しましょう!
参考
Discussion