レスポンシブデザインのページで、 window.innerWidth
の値によって条件分岐するような js を書いていたとき、 iOS だけ条件分岐がうまくいってないことが判明。
ちなみに、 Chrome DevTools で iOS 系のデバイスモードにしているときと Android は 問題なく、iOS 実機及び iOS Simulator でのみ発生していました。
画面幅に関連する値を取得してみる
まずは計測ということで、以下のような HTML を用意して画面幅に関する3つの値、 window.innerWidth
と screen.width
と document.documentElement.clientWidth
を取得してみました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>viewport test</title>
</head>
<body>
<h1>viewport test</h1>
<p id="text"></p>
<script>
window.addEventListener('load', function(){
function printWidth(){
document.getElementById('text').innerHTML =
'window.innerWidth: '+window.innerWidth+
'<br>screen.width: '+screen.width+
'<br>documentElement.clientWidth: '+document.documentElement.clientWidth;
}
printWidth();
});
</script>
</body>
</html>
iOS simulator の結果(iOS9.2 iPhone6)
window.innerWidth
が 980 と、document.documentElement.clientWidth
の 375 とは大幅に違う値を示しています。
ここで、 safari のコンソールから window.innerWidth
を取得してみると、
?!?!
なぜ?!?!?!
Chrome DevTools の結果(iPhone6)
こっちは想定どおりの値です。
コンソールから window.innerWidth
を取得しても、
納得の結果です。
そもそも window.innerWidth とか document.documentElement.clientWidth って何の値なんだ
window.innerWidth
は MDN によると、
垂直スクロールバー(表示されている場合)を含む、ブラウザウィンドウの ビューポート (viewport) の幅を返します。
とのこと。
一方、document.documentElement
は 同じく MDNによると、
document のルート要素 (HTML 文書の場合は
<html>
要素) を返します。
であり、それの clientWidth
ということは、同じく MDN によると、
clientWidth は element 内のピクセル単位の幅を返します。 paddingを含み、垂直スクロールバー(存在するならば)、border、marginを含みません。
なので、 document.documentElement.clientWidth
は <html>
要素の padding を含むが、垂直スクロールバー、border、 margin 含まない幅。
おおまかにいうと、垂直スクロールバーを含むか含まないかが違いそうです。
そのため、ブラウザによっては必ずしも全く同じではないことがわかります。
あと、document.documentElement.clientWidth
でもいいか、と思ってたけど、厳密に viewport で判定したいときは window.innerWidth
を使うべきだということもわかった。
ただし、今回スクロールバーは出てない文章量だし、980 と 375 っていう倍以上の差は一体何からうまれるのか…
色々試してみたら、一致するパターンがあった
色々と書き換えてみたところ、以下のように window.innerWidth
の前に document.documentElement.clientWidth
を取得してみたら、期待通りの値を取得できた。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>viewport test</title>
</head>
<body>
<h1>viewport test</h1>
<p id="text"></p>
<script>
window.addEventListener('load', function(){
function printWidth(){
document.getElementById('text').innerHTML =
'documentElement.clientWidth: '+document.documentElement.clientWidth+
'<br>screen.width: '+screen.width+
'<br>window.innerWidth: '+window.innerWidth;
}
printWidth();
});
</script>
</body>
</html>
iOS simulator の結果(iOS9.2 iPhone6)
Chrome DevTools の結果(iPhone6)
まとめ
iOS で window.innerWidth
の取得値が期待通りにいかないときは、先に document.documentElement.clientWidth
を取得するとうまくいくかもよ。
なぜなのか
わかりません。というかこの現象は必ず起こるものなのか。
どなたかご存知の方がいらっしゃいましたらコメントなどでお知らせ頂けますと助かります。