iOS safari 9.2 で window.innerWidth と viewport の値がズレる

レスポンシブデザインのページで、 `window.innerWidth` の値によって条件分岐するような js を書いていたとき、 iOS だけ条件分岐がうまくいってないことが判明。
ちなみに、 Chrome DevTools で iOS 系のデバイスモードにしているときと Android は 問題なく、iOS 実機及び iOS Simulator でのみ発生していました。

## 画面幅に関連する値を取得してみる

まずは計測ということで、以下のような HTML を用意して画面幅に関する3つの値、 `window.innerWidth` と `screen.width` と `document.documentElement.clientWidth` を取得してみました。

```html
<!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)

![simulator_1.png](https://qiita-image-store.s3.amazonaws.com/0/26559/5c9ba43f-ea78-6341-25e9-87beb3751f8b.png "simulator_1.png")


`window.innerWidth` が 980 と、`document.documentElement.clientWidth` の 375 とは大幅に違う値を示しています。
ここで、 safari のコンソールから `window.innerWidth` を取得してみると、

![simulator_1_a.png](https://qiita-image-store.s3.amazonaws.com/0/26559/33752820-1206-6c40-5b0d-34e1512acfd7.png "simulator_1_a.png")

？！？！
なぜ？！？！？！

### Chrome DevTools の結果(iPhone6)

![chrome_1.png](https://qiita-image-store.s3.amazonaws.com/0/26559/679acf2f-babb-4d88-4abd-7a2a8e5ca4da.png "chrome_1.png")

こっちは想定どおりの値です。
コンソールから `window.innerWidth` を取得しても、

![chrome_1_a.png](https://qiita-image-store.s3.amazonaws.com/0/26559/746a3283-5ee7-0678-a132-97600c3a711f.png "chrome_1_a.png")

納得の結果です。

## そもそも window.innerWidth とか document.documentElement.clientWidth って何の値なんだ

`window.innerWidth` は [MDN](https://developer.mozilla.org/ja/docs/Web/API/Window/innerWidth) によると、

> 垂直スクロールバー（表示されている場合）を含む、ブラウザウィンドウの ビューポート (viewport) の幅を返します。

とのこと。

一方、`document.documentElement` は 同じく [MDN](https://developer.mozilla.org/ja/docs/Web/API/Document/documentElement)によると、

> document のルート要素 (HTML 文書の場合は `<html>` 要素) を返します。

であり、それの `clientWidth` ということは、同じく [MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/clientWidth) によると、

> 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` を取得してみたら、期待通りの値を取得できた。

```html
<!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)

![simulator_2.png](https://qiita-image-store.s3.amazonaws.com/0/26559/a06ab120-f652-2fc7-d9ed-6cedb3cade39.png "simulator_2.png")

### Chrome DevTools の結果(iPhone6)

![chrome_2.png](https://qiita-image-store.s3.amazonaws.com/0/26559/840a6b18-e51c-248a-24bb-29b684feb566.png "chrome_2.png")

## まとめ

iOS で `window.innerWidth` の取得値が期待通りにいかないときは、先に `document.documentElement.clientWidth` を取得するとうまくいくかもよ。

### なぜなのか

わかりません。というかこの現象は必ず起こるものなのか。
どなたかご存知の方がいらっしゃいましたらコメントなどでお知らせ頂けますと助かります。