JavaScript

スマホで100vhがはみ出るときの対策(ios,android)

100vhを指定してもスマホではアドレスバーの分だけはみ出ることがあります。100dvh・100svhでCSSだけで直す方法と、古いブラウザ向けにJavaScriptで1vhを計算し直す方法を紹介します。

この記事の目次
  1. 100vhがスマホではみ出るときの対策
  2. CSSだけで済ませる方法(100dvh / 100svh)
  3. JavaScriptで1vhを計算し直す方法
  4. HTML
  5. CSS
  6. JavaScript
  7. JavaScriptの解説
  8. リサイズの解説

100vhを設定しても、スマホで確認すると100vhよりはみ出るときがあります。
とくにiosなどではアドレスバー部分の影響で100vhなのに100vhにならないことがあります。

その場合の対策を紹介します。

100vhとは「ビューポート(ブラウザの表示領域)の高さの100%」を表すCSSの単位です。ただしスマホのブラウザは、アドレスバーを表示した状態ではなく隠した状態の高さを100vhとして計算します。そのため、アドレスバーが出ている間は画面より縦に長くなり、下がはみ出します。

結論として、現在は新しいCSS単位の100dvh(または100svh)を使えばCSSだけで解決できます。古いブラウザも対象にする場合は、この記事のJavaScriptで1vhを計算し直す方法を組み合わせます。

100vhがスマホではみ出るときの対策

CSSだけで済ませる方法(100dvh / 100svh)

ビューポート単位の仕様には、アドレスバーの表示状態に合わせて変わるdvh、常に小さいほうの高さを取るsvh、大きいほうを取るlvhが追加されています。従来のvhlvhと同じ扱いです。

#top {
  height: 100vh;  /* 未対応ブラウザ向けの予備 */
  height: 100svh; /* アドレスバー表示時の高さに固定 */
}

dvhはスクロール中にアドレスバーが出入りするたびに高さが変わるため、中身が動いて見えることがあります。ファーストビューを固定したいだけなら、変動しないsvhのほうが扱いやすいです。2022年以降の主要ブラウザで使えますが、対応状況は実装時に確認してください。

JavaScriptで1vhを計算し直す方法

上の単位が使えない環境も含めて揃えたい場合は、以下のJavaScriptとCSSを組み合わせます。

HTML

<section id="top">
  ここにコンテンツ
</section>

コンテンツやidは参考です。

CSS

#top {
  height: 100vh; /* Fallback */
  height: calc(var(--vh, 1vh) * 100);
}

下のheightが本命で、JavaScriptでCSS変数–vhの値をデバイスごとに決めます。
上のheightは、calc部分に対応していない場合のための予備です。

JavaScript

const setFillHeight = () => {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
}

// ここからリサイズの対応
let vw = window.innerWidth;
window.addEventListener('resize', () => {
  if (vw === window.innerWidth) {
  // 画面の横幅にサイズ変動がないので処理を終える
    return;
  }

  // 画面の横幅のサイズ変動があった時のみ高さを再計算する
  vw = window.innerWidth;
  setFillHeight();
});

// 実行
setFillHeight();

JavaScriptの解説

考え方としては、
デバイスの高さを取得して、そこから1vhの値を算出することで、
どのデバイスでも100vhをはみ出さずに表示させます。

まずconstから始まる部分でsetFillHeightという関数を定義します。
デバイスの高さは、window.innerHeightで取得できるので、それをパーセントの値に変換します。それが、1vhとなります。
あとは、その1vhの値をcssの変数の値として適用させることで1vhがデバイスごとに反映されます。

リサイズの解説

iOSなどのスマホでは、スクロールすると
アドレスバーが表示されたり非表示になったりします。
その関係でビューポートの高さ、つまり表示されるコンテンツの画面の高さが変更されます。

高さが変化した場合でもリサイズしたい場合は、

window.addEventListener('resize',setFillHeight);

でOKです。

ただ、それだと100vhがスクロールするたびに変化するので、
それを避けたい場合は、上の例のようにwindow.innerWidthを取得して、
その値が変わったときだけ再計算します。

そうすることで、アドレスバーが出たり入ったりしてもリサイズされません。

ファーストビューの表示が遅い原因がフォントの読み込みにある場合は、Google Fontsの読み込みを軽くする方法もあわせて確認してください。

スポンサーリンク