【解決法】iOS12+ブラウザ(Safari / Chrome)で100vhの設定が想定どおりに動かない件

アイキャッチ:ドロップダウンメニューが見切れてクリックできない CSS

おそらく初心者あるあるシリーズの一つ。

モバイルとPC双方に対応したいわゆる「レスポンシブサイト」を作ったものの、モバイル端末での表示が崩れる。

私も作品を展示するためのサイトを作る際に見事引っかかりました。

作成したサイトをPCで表示した場合(ブラウザ:Google Chrome)
作成したサイトをPCで表示した場合(ブラウザ:Google Chrome)
同じサイトをiPad Proで表示した場合(iOSのChrome)。見切れたドロップダウンメニューに注目。どうしてこうなった( ^ω^ #)
同じサイトをiPad Proで表示した場合(iOSのChrome)。見切れたドロップダウンメニューに注目。どうしてこうなった( ^ω^ #)

PCでは想定通りに表示されるのに、手持ちのiPad Pro(OSは最新版に更新しているためiOS12)での表示がおかしくなってしまったのです。Webページ全体(例えばbodyタグやページ全体の要素を包むdivタグ)の高さをCSSで「height: 100vh」で設定すると、なぜか画面の下側がはみ出してしまうのです。

すぐに解決策を見たい方は→こちら

※注:手元にあるモバイル端末がiOS12入りiPad Pro(10.5 inch)のみであることから、より古いOS、iPad Pro第2世代 (10.5 inch)以外の端末(特にandroid OS系端末)についてはどうなるのか確認できません。
ただ、iOS系端末では2015年頃からheight:100vh設定の挙動がおかしいと報告されていることを確認しています(Webkit系ブラウザであるSafariとChromeいずれでも発生)。また、android+Chromeでも同様の不具合があるようです

さて、今回画面下部に言語設定のドロップダウンメニューを設置したため、この問題は個人的にかなり深刻です。
一応スクロールすれば出てくるとはいえ、とっさにわからないだろうと…。
特に言語設定を間違えた場合、ものすご~く困ります(´・_・`)ガッカリ

というわけで、

全体の高さを「height: 100vh」で設定すると、なぜか一部が下の方にはみ出してしまう問題

をどうやって解決するかについて説明します。

まずは、お約束のGoogle先生に質問。
すると、iOSで100vhを設定したら表示が崩れる原因を説明しているページに出くわします。

ところ…が……………!
件の記事によると(特に「February 23rd update」以降の記述に注目)

ブラウザを開発する側に、100vh問題を解決する意志は皆無、と…(´・_・`)ガッカリ

ま…まぢで…!? 困ってる人結構いるのに…。

とまあ、絶望してる場合じゃないので、いろいろと探した結果、解決策と思しきものはいくつか出てきましたが、最終的に以下の方法で問題なく表示できるようになりました。

Javascriptのコード

window.onresize = function() {
let vh = document.documentElement.clientHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
window.onresize();

CSSのコード

html, body, #wrapper { // ←サイト全体の高さを決めるタグ
width: 100%;
height: 100vh; // ←calc関数やCSSの変数に対応していないブラウザ対策のようです
height: calc(var(--vh, 1vh) * 100); // ←*の後の数字は本来設定したいvhの値に合わせて変更
}

太字で表示した部分が重要です。
特にdocument.documentElement.clientHeightの部分。

Javascriptを使って(前略)clientHeightを100で割った数値=1vh相当の値を元に、CSSの変数calc関数を使って実際の100vhに相当する数値を算出した物を本来のvhの代わりに使います。

ウィンドウサイズが変更される度(スマートフォンなどの画面の向きを変えるのも含む)、計算したものを適用するというスクリプトです。

.。oO{全体のvh部分を修正しても表示が崩れた場合は各パーツのvh部分も書き換えるといいかもしれない}

実は、innerHeightって…。

何が何でもiOS系ブラウザについてくるアドレスバーなどの部分を外して計算した数値を押し付けようとしてしまいます。

Javascriptで測定して検証したところ、iOS系ブラウザのinnerHeightはサイトを読み込んだ直後の(アドレスバーがある状態の)画面サイズより大きい高さを出してきています。
そのため、こいつを基準にして計算している限り、確実に表示が崩れてしまいます

そんな残念なサンプルがこちらになります。
何故かwindow.innerHeightの数値更新が遅れます(笑)が、画面を下の方にスクロールしてみればwindow.innerHeightだけ数値が変動することがわかるかと思います。

…というのも、Google先生に「iOSの100vh問題について」聞くと候補として表示されるサイト(後述)には、clientHeightではなくwindow.innerHeightを使う記述が多かったのですが…。

少なくともiOS12+Safari or Chromeでは全く改善しませんでした😢
前述の残念なサンプルを見ての通りです(ただしPC経由では正常に表示されます)。

対策を施したサンプルがこちらになります。

iOS経由でまともな表示になったサイト(画面下部のドロップダウンメニューに注目)
iOS経由でまともな表示になったサイト(画面下部のドロップダウンメニューに注目)

<参考サイト>

ちなみにclientHeightに計算対象とするブロックのheightとpaddingは含まれますが、marginは含まれないことに注意です。

確かbox-sizing: border-boxと似たような感じ。