【CSS】100vhは「一画面」ではないという罠
こんにちは!今日はCSSの単位の一つ「vh」の厄介すぎる仕様について話したいと思います。
vh
vh(view height)はCSS3で策定された単位の一つで、ざっくり言うと画面の高さです。ちなみ横幅はvw(view width)です。
vhは百分率で表されます。50vhなら画面の見える高さの半分ですね。 縦幅1000pxのスマホなら、 画面の高さ = 100vh = 1000px
よく使うケースとしては画面と同じ大きさに表示したい時です。クラス.fooを持つ要素をちょうど1画面ぶんいっぱいに表示したい時は…
.foo {
width: 100vw;
height: 100vh;
}
といった感じです。
画面と同じ大きさなら100%でよくない?
…って思った人。その通りです!
ですが、 %はあくまで親要素のn%ぶんの長さ。例えばこのようにクラス.childのheightが100%だった場合、
.child {
height: 100%;
}
このchildの画面上での高さは親要素の100%(親要素と同じ長さ)。つまり親要素の高さを見なければ分かりません。しかし悲しいかな、親要素の長さの単位も%だったりする場合が往々にしてあるわけです。こんな感じに…
.parent {
height: 50%;
}
.child {
height: 100%;
}
この場合は更に親要素の確認が必要です。こんなケースは結構あるんじゃないでしょうかね? 端的に言えば、サイズが親に依存するわけです。CSSが密結合になるって言うのかな?これは良くないですね。一方、vhは…
.child {
height: 100vh;
}
100vhならば、親がいようとなかろうと100vhです。100vhは、必ず画面と同じ高さです。
vhは適切に用いることでCSSをシンプルにします。特にページのファーストビューに対して100vhを指定するのは とても良いアイデアだと思います。そんな素敵なvhですが、思わぬ罠が…
100vhは「一画面」ではない
vhは画面の高さです。100vhの高さは必ずあなたのスマホの液晶と同じ高さです。…が、vhの罠の一つとして一画面でない点が挙げられます。 画面の縦幅が1000pxであれば、常に「100vh = 1000px = ぴったり一画面」 であると思いませんか?しかし、そうではないのです…
アドレスバーの分押し出される
このページをスマホで見ている人は画面上の方にURLとか書いてませんか?いわゆるアドレスバーと呼ばれる部分です。
アドレスバーはWebサイトでは(現在は)必ず表示され、Webサイト本体が表示される領域を狭めます。例えば縦1000pxのスマホに60pxのアドレスバーがあった場合、サイト一画面は 1000px - 60px = 940px となります。
この時、画面いっぱいに表示したい要素は縦何pxにするべきでしょうか?? 勿論940pxですね!なぜなら1000pxのうち60pxはアドレスバーだからです。
一方、アドレスバーがあってもなくても100vhが示すのは1000pxのままです。それもそのはず、vhは画面の高さ、であり、画面のうちコンテンツが表示される部分の高さではないのです。したがって、この場合画面いっぱいに表示したい要素に100vhを付けると60pxぶん画面からはみ出します。
100vh ≠ 一画面
100vh - アドレスバー = 一画面
ならば、最初からアドレスバーの分を削って940pxくらいにしたらどうでしょう?
残影ながらこれも駄目。アドレスバーは厄介なことに大抵の機種ではスクロールすると引っ込むんです… 当然、アドレスバーが引っ込んだ後では940pxで画面全体は覆えません。
追い打ちをかけると、そもそもアドレスバーの長さは機種によりまちまちです。
この仕様の被害を最も被るのがファーストビューでしょう。ページを開いた瞬間に画面一杯に表示したい画像に height: 100vh を指定するとメニューバーの分は見切れてしまいます。
キーボードが出ると圧縮される
スマホで文字入力をする時に使うキーボードもvhは含みません。フォーム系のレイアウトにvhを使うと、 文字を入力しようとした瞬間にフォームが急激に縮むカオスな状況が見られます。
問題と解決策
これらのケースから、vhの問題はスマホにおいてアドレスバー等を考慮しない点と言えるでしょう。
そして、これを解決するために必要なのは 画面の高さではなく画面のうちコンテンツが表示される部分の高さを知る方法、だということが言えます。
というわけで次回は「ファーストビューを綺麗に出すCSS+JavaScriptテクニック」みたいな内容を書きたいと思います。おわり!
【追記】(2019/05/22)一部修正しました。続き