🤳

iOSでも100vhをいい具合に調整して画面の高さいっぱいに要素を表示させる

2020/10/03に公開
2

TAK(@tak_dcxi)です。今回もCSSに関する投稿です。

以前このようなツイートをしました。

メインビジュアルなど、画面いっぱいに要素を表示するためにheightmin-height100vhを指定する。そして、iOSで表示確認した時に以下のような問題が起こるわけです…。

iOSのSafariでの100vhが気に食わない問題

iOSのSafariでは100vhの計算にアドレスバーが考慮されていないため、アドレスバー分押し出されて格好悪く表示されます。ちなみにiOSのGoogle Chromeは中身SafariなのであれもSafariです。

この問題に立ち向かうために、実装者はJavaScriptを利用して高さを指定したり、height: 100%;のバケツリレーを行ってアドレスバーまで考慮した画面いっぱいの表示を実現するために頑張ってきたわけです。

そんな中、先程のツイートからちょうど数日後、新しく発見されたテクニックが実装者を沸かせました。

画面いっぱいに表示したい要素の高さに-webkit-fill-availableを指定する。これだけで「iOSのSafariでの100vhが気に食わない問題」が解決できるようになったのです!

めでたしめでたし。おわり。

-webkit-fill-available、不安定問題

まず、これiOSではないGoogle Chromeにも認識されます

上記の-webkit-fill-availableを指定するテクニックが発表された当時は問題なかったのですが、暫くしてChromeのアップデートにより仕様が変わったらしく、デスクトップ版のChromeで表示が崩れる現象が報告されました

そういった理由から、このテクニックを用いる場合は以下のような指定もセットで行われるようになっています。

/* Avoid Chrome to see Safari hack */
@supports (-webkit-touch-callout: none) {
  body {
    /* The hack for Safari */
    height: -webkit-fill-available;
  }
}

Chromeでは作動させないようにするハックですね。

加えてデバイスの向きを変えた時など、画面の高さに変動があった際に再描写がされないので最初に表示された高さで固定されるといった懸念点もあります。つまり、iPhoneやiPadで横向き時にアクセスして、その後縦向きに戻したとしてもその要素の高さは横向き時のままで見栄えが悪くなるかもしれないということです。

従来どおりJavaScriptで高さを計算したほうが安定している件

以上のように-webkit-fill-availableは色々と不安定なので、従来のようにJSで計算して高さを描写したほうが確実そうです。

そこで僕がオススメしたいのはCSS変数を用いたテクニックです。

<div class="hero-header">
  <!-- この要素を画面いっぱいに表示したい -->
</div>
.hero-header {
  min-height: 100vh; /* Fallback */
  min-height: calc(var(--vh, 1vh) * 100);
}
const setFillHeight = () => {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
}

// 画面のサイズ変動があった時に高さを再計算する
window.addEventListener('resize', setFillHeight);

// 初期化
setFillHeight();

以上のような指定を行うことで

  • モバイル端末でもデスクトップでも崩れることなく画面いっぱいに要素を表示することができる
  • 画面の高さに変動があった際も高さを再描写してくれる

といった-webkit-fill-availableの懸念点を払拭できます。

また、CSS変数を用いているので、他の要素を画面いっぱいの高さで表示したいって時も使いまわしが容易です

ただ、CSS変数はレガシーブラウザ(IEとかIEとかIEとか)には非対応のため、フォールバックとしてmin-height: 100vh;も指定しておきましょう

サンプルでは要素内のコンテンツの高さがユーザーの画面の高さを超えた時を意識してmin-heightを使っていますが、heightでもOKです。

ただ、上記の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();

ただし、高さのみ変動があった場合には再描写は行われないのでご注意。

まとめ

  • あまりハック的なことはやらないで安定した方法を使ったほうがいいのでは?
  • CSS変数はJavaScriptやメディアクエリで値を変えられるのでとても便利
  • IEさっさとくたばれ

もちろん、-webkit-fill-availableを利用する方法にもJSを使わない分軽量であるというメリットがあります。-webkit-fill-availableを利用する方法と今回紹介したCSS変数を利用する方法、どちらを採用するかは案件と相談してください

Discussion

voidvoid

非常に悩んでいたので助かりました!
こちらの環境では-webkit-fill-availableで解決できず、jsの調整で上手くいきました。
ありがとうございました。