Back

ResizeObserver vs Window Resize:それぞれをいつ使うべきか

ResizeObserver vs Window Resize:それぞれをいつ使うべきか

チャートコンポーネントを構築していて、コンテナが縮小したときに再描画する必要があるとします。window.addEventListener('resize', ...) を使用すると、ほとんどの場合うまく機能しますが、サイドバーが折りたたまれてパネルがリサイズされたときには何も発火しません。これが、この記事で扱う核心的な問題です。

重要なポイント

  • window resize イベントは、ブラウザのビューポートがサイズ変更されたときにのみ発火します。個々のDOM要素については何も知りません。
  • ResizeObserver は特定の要素を監視し、原因に関係なく、その寸法が変更されるたびに発火します。
  • ビューポートのリサイズ後に要素のサイズをポーリングする方法は脆弱です。CSSトグル、動的コンテンツ、フォント読み込みなどによる変更を見逃します。
  • JavaScriptを使わないコンテナベースのスタイリングには、CSSコンテナクエリが推奨されるツールです。

各APIが実際に監視するもの

window resize イベントは、ブラウザのビューポートがサイズ変更されたときに発火します。それがこのイベントの全範囲です。個々のDOM要素については何も知りません。

ResizeObserver特定の要素を監視し、原因に関係なく、その寸法が変更されるたびに発火します。ビューポートのリサイズ、DOM挿入、CSSクラスのトグル、親のflexboxリフローなど、これらすべてが要素のサイズを変更する場合、オブザーバーをトリガーします。

これが根本的な違いです。それぞれ異なるものを監視し、通常は異なる目的で選択されます。

Window Resizeイベントを使うべきとき

window resize イベントは、ロジックが特定の要素のサイズではなく、ビューポートの寸法に真に依存している場合に適切なツールです:

  • ビューポートのブレークポイントでモバイルナビゲーションレイアウトを切り替える
  • フルページのcanvasまたはWebGLコンテキストを window.innerWidth / window.innerHeight にリサイズする
  • 画面の表示領域に依存するグローバルレイアウト状態を調整する
window.addEventListener('resize', () => {
  canvas.width = window.innerWidth
  canvas.height = window.innerHeight
  render()
})

純粋に視覚的な、CSSで駆動されるビューポートサイズへの応答には、JavaScriptよりもメディアクエリを優先してください。

ResizeObserverを使うべきとき

ResizeObserverのユースケースは、要素の実際のレンダリングサイズに応答する必要があるコンポーネントレベルの動作を中心としています:

  • コンテナの幅が変更されたときに軸のスケールを再計算するチャート
  • 利用可能なスペースに基づいてツールバーのレイアウトを調整するテキストエディタ
  • リサイズ可能なパネルまたは分割ペインウィジェット
  • 独自のコンテナを制御しない埋め込みウィジェット
const ro = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { inlineSize, blockSize } = entry.contentBoxSize[0]
    chart.resize(inlineSize, blockSize)
  }
})

ro.observe(document.querySelector('.chart-container'))

最新のAPI形式を読み取る

現在のResizeObserver APIは、各エントリに contentBoxSizeborderBoxSize 配列を公開しています。それぞれに inlineSize(水平書字モードでの幅)と blockSize(高さ)が含まれています。古い contentRect プロパティも機能しますが、主に後方互換性のために存在します。新しいコードでは contentBoxSize[0] を優先してください。

Window Resizeの後にポーリングするだけではダメな理由

よくあるパターン—window resize イベントをリッスンし、その後要素に対して getBoundingClientRect() を呼び出す—は脆弱です。ビューポートとは関係のないサイズ変更を見逃します:動的コンテンツの読み込み、CSSクラスのトグル、子要素の挿入、フォントの読み込みなどです。ResizeObserverはこれらすべてをキャッチします。ビューポートのリサイズ後のポーリングではキャッチできません。

パフォーマンスとフィードバックループ

ResizeObserverのコールバックはレイアウト後、ペイント前に実行されます。つまり、コールバック内のDOM読み取りは、リフローを強制することなく現在のレイアウトを反映します。これは、resize イベントハンドラ内でレイアウトプロパティを読み取るよりも構造的に安全です。

1つの注意点:監視されている要素のサイズを独自のコールバック内で変更すると、別の監視がトリガーされます。ResizeObserverは、同じフレーム内でより深いDOM深度で監視を処理し、最終的にサイクルが解決しない場合はエラーを報告することで、これを処理します—無限ループを防ぎます。それでも、コールバック内に何を書くかについては慎重であるべきです。

ResizeObserverのパフォーマンス上の利点は、コールバックがレイアウトに触れる場合、または要素のサイズ変更がビューポートのリサイズとは独立して発生する場合に意味を持ちます。

CSSコンテナクエリという代替手段

目標がコンテナのサイズに基づいて要素をスタイリングすることである場合、CSSコンテナクエリが適切なツールです—JavaScriptは不要です。ResizeObserverは、スタイリングではなく、ランタイムロジックがサイズ変更に応答する必要がある場合に使用してください。

シナリオ最適なツール
ビューポート全体のレイアウト変更window resize イベント
要素の寸法がJSロジックを駆動するResizeObserver
コンテナベースのスタイリングのみCSSコンテナクエリ

まとめ

ビューポートを気にする場合は window resize イベントを使用してください。要素を気にする場合はResizeObserverを使用してください。スタイルを変更するだけの場合はCSSコンテナクエリを使用してください。これら3つのツールは、レスポンシブな動作の全領域をカバーします—実際に測定しているものに合ったものを選択してください。

よくある質問

ResizeObserverは要素の寸法が変更されたときに発火するため、要素を幅または高さゼロに折りたたむとトリガーされます。ただし、寸法をそのままにしてvisibilityやopacityの変更では発火しません。真の可視性検出には、代わりにIntersectionObserverを使用してください。

オブザーバーは、要素がガベージコレクションされると自動的に追跡を停止します。ただし、特にシングルページアプリケーションやマウント/アンマウントライフサイクルを持つフレームワークコンポーネントでは、古い参照や予期しないコールバックを避けるために、明示的にunobserveまたはdisconnectを呼び出すことが良い習慣です。

はい。ResizeObserverは、Chrome、Firefox、Safari、Edgeで広くサポートされています。古いブラウザをサポートする必要がある場合は、ポリフィルが利用可能ですが、内部的にポーリングとmutation observersに依存しているため、ネイティブのパフォーマンスには一致しません。

通常、軽量なUI更新には不要です。ResizeObserverは、レンダリングパイプライン内で通知を効率的にバッチ処理します。ただし、コールバックがネットワークリクエストや重い計算などの高コストな下流作業をトリガーする場合は、コールバック全体ではなく、その特定の作業をデバウンスしてください。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay