デバイスの画面幅からはみ出している要素を検出する方法（２）

レスポンシブ対応のデバッグする際、デバイスの画面幅からはみ出して横スクロールの原因となっている要素がないかJavaScriptを使ってチェックしたい。&mdash;&mdash;記事の後編です。

[前編](https://qiita.com/kazhashimoto/items/d91a9683972a6a508050)で作った関数`getDeviceWidth()`を使って、実際に要素のはみ出しをチェックしてみましょう。

レスポンシブ対応がお粗末なサンプルを用意しました：
https://codepen.io/kaz_hashimoto/pen/rNvjwBb

デモの動画です。これはiPad ProのSplit Viewで表示した例です。Split Viewで割り当てられた領域の幅を「画面幅」として、その幅をはみ出してレイアウトされている要素を赤枠で表示しています。実際にはビューポートの幅がSplit Viewの幅にスケーリングされページがフィットして表示されています。

<p class="codepen" data-height="618.8986206054688" data-default-tab="result" data-slug-hash="ExLZoRo" data-user="kaz_hashimoto" style="height: 618.8986206054688px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kaz_hashimoto/pen/ExLZoRo">
  [video] detect overflow elements</a> by Kazuhiro Hashimoto (<a href="https://codepen.io/kaz_hashimoto">@kaz_hashimoto</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

※画像引用：Photo by [Inspirationfeed](https://unsplash.com/@inspirationfeed?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)

要素のはみ出しをチェックするコードです。

```javascript:javascript（前編からの再掲）
function getDeviceWidth() {
  if (typeof window.visualViewport === 'undefined') {
    return window.innerWidth;
  }
  const width = visualViewport.width * visualViewport.scale;
  return Math.round(width);
}
```

```javascript:javascript
let enable_check_overflow = true;
(function check_overflow() {
  if (!enable_check_overflow) {
    return;
  }
  const device_width = getDeviceWidth();

  // const ignore = document.body.querySelectorAll('.slick-slide:not(*)');
  const ignore = document.body.querySelectorAll('.slick-slide, .slider');
  const list = document.body.querySelectorAll('*');
  for (const el of list) {
    if (el.classList.contains('__overflow')) {
      continue;
    }
    let exclude = false;
    for (const o of ignore) {
      if (o.contains(el)) {
        exclude = true;
        break;
      }
    }
    if (exclude) {
      continue;
    }
    ['width', 'height'].forEach(a => {
      if (el.hasAttribute(a)) {
        el.removeAttribute(a);
      }
    });

    let checked = false;
    const r = el.getBoundingClientRect();
    if (r.width > device_width) {
      console.log('## overflow width', el);
      checked = true;
    } else if (r.right > device_width) {
      console.log('## overflow right', el);
      checked = true;
    }
    if (checked) {
      el.classList.add('__overflow');
    }
  }
  setTimeout(check_overflow, 5000);
})();
```

`check_overflow()`は５秒おきに`body`内の要素をスキャンして、要素のバウンディングボックスの寸法をチェックします。ボックスの幅がデバイスの画面幅を超えていた場合やボックスの右端が画面幅を超えていた場合には、consoleにメッセージとElementを出力し、その要素にclass `__overflow`をフラグとして立てます。
注）`__overflow`は一度ONになると解除されません。

img要素などHTMLに`width`や`height`属性が指定されている場合は、それらを削除してチェックします。CSSでの設定漏れを検出するためです。

このサンプルでは、slickスライダーの画像はチェックの対象外としました。変数`ignore`にセットしている`querySelectorAll`の箇所です。スライダーの画像は元々画面外に配置されており、チェックで毎回検出されてconsoleの出力が鬱陶しくなるためです。
この機能をオフにするには、コメント行に示したように`ignore`に空のNodeListがセットされるようにします。

はみ出しが検出された要素を画面上で見分けやすくするために、このサンプルでは`__overflow`セレクターに対して`outline`を指定して輪郭線を表示させています。

```css:css
.__overflow {
  outline: 2px solid red;
  outline-offset: -2px;
}
```

`check_overflow()`の用途はデバッグに限られるため、コーディング中にちょっと表示をチェックしたい時だけDevToolsのconsoleに上記のスニペットをコピペして実行するという使い方でも十分です。