【CSS:vh】PC・Android、iPhoneでのviewport height指定時の高さの違いと対策
PC、Android、iPhoneで「vh」の高さが違い、見事にハマったことがあるので、記事としてまとめておきたいと思います。
vh(viewport height)とは
「vh」は「viewport height」の略で、viewport の高さ(ブラウザの高さ)に対する割合を指定することが可能です。 100vhを指定すると、画面いっぱいに広がってくれるので重宝する一方で、デバイスごとに注意しなければ微妙なスタイルの崩れが発生します。
PCブラウザでvhの高さを測定する
<figure class="figure-image figure-image-fotolife" title="PCブラウザでのvh"><figcaption>PCブラウザでのvh</figcaption></figure>
innerHeightは、ウィンドウの内部の高さを取得するプロパティで、100vh
はこれと同じ値 つまりブラウザ内部の高さいっぱいに広がってくれています。
innerHeight は Window インターフェイスの読み取り専用プロパティで、ウィンドウの内部の高さをピクセル単位で返します。水平スクロールバーがあれば、その高さを含みます。 innerHeight の値はウィンドウのレイアウトビューポート (en-US)の高さから取られます。幅は innerWidth プロパティを使用して取ることができます。 ウィンドウから水平スクロールバーや境界を引いた高さを取得するには、ルートの <html> 要素の clientHeight() プロパティを代わりに使用してください。 Window | innerHeight
スマホでvhの高さを測定する
スマホで100vh
を指定した場合、アドレスバーの部分が計算にはいらず、アドレスバー分押し出される形になるので、注意が必要。
アドレスバー分押し出される影響は
-
想定しないスクロールが発生する
-
アドレスバー分、中央配置の要素が少しずれる
対策
CSS変数を使う方法
CSSで-webkit-fill-available
を指定して解決する記事がたくさんありますが、このプロパティは不安定であり、デスクトップ版のChromeで表示が崩れたり、画面サイズに変更があった場合に対応できないという問題があった。
TAKさんの記事では、JavaScriptで高さを計算し、CSS変数を使って対応する方法が書かれています。
CSS変数なので使い回しが簡単という点がイケてる。
JavaScriptで高さを直接指定する
前述したAndroidとiPhoneの例を見ると、100vh
を指定した際に、innerHeight
の領域外まで広がってしまうのが問題のように感じた。
であれば、画面いっぱいに広げたい要素のラッパーをinnerHeight
の高さに合わせて上げれば解決する。
以下は、ReactのuseRefを使った実装例
import {useRef,useCallback,useEffect} from 'react';
import './App.css';
function App() {
const fullHeightRef = useRef<HTMLDivElement>(null)
// 画面いっぱいに表示させたい要素にRefを指定
const setHeight = useCallback(() => {
if(fullHeightRef.current) fullHeightRef.current.style.height = `${window.innerHeight}px`
}, [])
useEffect(() => {
setHeight()
window.addEventListener('resize', setHeight)
return () => {
window.removeEventListener('resize', setHeight)
}
}, [setHeight])
return (
<div className="App" id="App" ref={fullHeightRef}>
<div className="App__inner">
画面いっぱいに広げたいコンテンツ
</div>
</div>
);
}
export default App;
とても微妙な調整ではあるが、これがフロントエンドの仕事でもある。 マルチデバイス対応はプロフェッショナルとして意識していきたい。