Android / iOS 環境を考慮した画面の高さいっぱいの Height 指定方法(背景色の考慮も添えて)

Android / iOS 環境を考慮した画面の高さいっぱいの Height 指定方法(背景色の考慮も添えて)

November 2, 2021

Android と iOS のそれぞれの環境を考慮して、画面の高さいっぱいに要素を表示させる方法です。

実行環境で高さの解釈が異なる #

100% や 100vh の解釈がそれぞれの環境で異なります。

なおこれは OS やブラウザのバージョンによりますので、将来的には本記事から状況が変わっている可能性も大きいです。

本記事においては、Android バージョン 11 、iOS バージョン 15 の環境を想定しています。

Android × Chrome #

Android 11 は画面上部にアドレスバー(URL 表示部分)が表示されます。

  • 100vh はスクリーンの高さ
  • 画面にはアドレスバーも表示されるため、要素を 100vh 指定しているときにアドレスバーが表示されていると、アドレスバーの高さ分だけ要素が画面外にはみ出してしまう。
  • 100% は表示領域の高さ
  • アドレスバーが表示されているときは、アドレスバーを除いた表示領域の高さが 100% と一致する。

Android としては 100% 指定を適用したい。

iOS × Safari #

iOS15 は 下にタブバー(URL 表示部分)が表示されます。

  • 100% はタブバーを除いた領域の高さ
  • タブバー領域は 100% の範囲外にあるので、要素を 100% 指定している時にタブバーが引っ込んでいると、その領域は空白になる。
  • 100vh は表示領域の高さ
  • 要素を 100vh 指定している時にタブバーが引っ込んでいると、その領域まで要素が表示される。

iOS としては 100vh 指定を適用したい。

まとめ(その1) #

両環境で望ましい指定は以下となりました。

  • Android としては 100% 指定を適用したい。
  • iOS としては 100vh 指定を適用したい。

背景色の指定を考慮する #

コーディングに入る前に1つだけ考慮したいことがあります。100% 指定についてです。

ある要素に 100% 指定を適用するにはその親要素が固定の高さを持っている必要があります。そのため html タグからすべて height: 100%; を適用しなければなりません。

ここで例えば html タグに背景色を指定すると、表示領域の高さのみに背景色が適用されるため、100% からはみ出したものが存在する時に、そこに背景色は適用されません。はみ出した要素1つ1つに背景色を指定することで回避できますが、手間がかかり現実的ではありません。

理想としては、高さ指定のない html タグが一番上に存在し、その下の body タグに固定の高さを持たせたいです。この html タグに背景色を指定しておけば、body タグの 100% からはみ出した領域についても背景色が適用されるようになります。

html タグからすべて 100% 指定を行う方法だとこれが実現できないため、JavaScript で innerHeight を取得して固定値として body タグにその高さを適用することで実現します。

なお iOS に innerHeight を適用した場合は、iOS に 100% を適用した場合と同様の結果になり、求めている表示とは違ってしまいます。

まとめ(その2) #

ここまでの内容から実現すべきことを改めて整理すると以下の通りです。

  • Android の場合は body タグに innerHeight の高さを適用する。
  • iOS の場合は body タグに 100vh を適用する。

実現方法(CSS / JavaScript) #

デバイスを判断して、iPhone なら body タグに 100vh を、それ以外には body タグに innerHeight を適用させます。

CSS カスタムプロパティを利用します。

CSS

html {
  /* 背景色は適当です。 */
  background-color: #ff6699;
}

body {
  /* JavaScript から動的に設定する。 */
  height: var(--height);
}

JavaScript

// ユーザーエージェントからデバイスが iPhone か否かを判断する。
const isIphone = () => {
  const ua = navigator.userAgent;
  const regexp = /iPhone.+Mobile/g;
  return regexp.exec(ua) !== null;
};

// CSS カスタムプロパティ "--height" に高さを設定する。
const setter = (height) => {
  const customProperty = '--height';
  document.documentElement.style.setProperty(customProperty, height);
};

// デバイスに応じて高さを設定する。
const setHeignt = () => {
  return isIphone() ? setter('100vh') : setter(`${window.innerHeight}px`);
};

// 高さを設定する。 - 初期描画時
setHeignt();

// 高さを再設定する。 - ウィンドウのリサイズ時
let timeoutId;
window.addEventListener('resize', () => {
  clearTimeout(timeoutId);
  timeoutId = window.setTimeout(setHeignt, 200); // タイマー値は適宜調整ください。
});

断続的にリサイズイベントが発火して処理が実行されすぎないようにタイマー値を持たせています。こうすることによって指定時間以内に連続してイベントが発火している場合、それらに対して処理は実行されず、最後に発生したイベントに対してのみ処理を行わせることが可能です。

ユーザーエージェントから iPhone デバイスを判定する部分については、将来的な iOS の仕様によっては修正を加える必要があるかもしれません。もしこの記事をご参考に作業される際は、上記コードで本当に iPhone が判別できているかご確認のうえご利用ください。

最後に #

そもそも、このようなことをせずとも全ての環境で高さいっぱいが適用される仕様を策定してほしいものですね。

次世代の CSS4 で svhlvhdvhといった高さ指定が利用可能となる可能性が高いため、これが現在の状況を解消してくれることに期待しましょう。