12k
All articles

フロントエンド開発者が知っておくべき5つのJavaScript API

フロントエンド向けの5つのJavaScriptネイティブAPI: IntersectionObserver、ResizeObserver、AbortController、Clipboard API、View Transitions.

OpenReplay Team
OpenReplay Team
フロントエンド開発者が知っておくべき5つのJavaScript API

現時点で最も習得する価値のあるネイティブブラウザAPIは、IntersectionObserver、ResizeObserver、AbortController、Clipboard API、そしてView Transitions APIの5つです。これらはいずれも、現行のすべてのブラウザに標準搭載されているプラットフォームプリミティブであり、一般的な外部依存を置き換えることができます。

多くの開発者は、コピーボタン一つのためにライブラリ全体を導入したり、古い結果を描画し続ける検索ボックスの対処に半日を費やしたりして、後になってブラウザ自体に解決策が備わっていたことに気づく、という経験をしています。fetchlocalStorage、DOMは使いこなしているものの、新しいオブザーバーやユーティリティAPIにまだ触れていないなら、このリストを参考にすることで一つか二つのライブラリを削除できるでしょう。以下で紹介するものはすべてネイティブWebプラットフォームAPIであり、TMDBやUnsplashのようなホスト型RESTサービスではありません。そのため、APIキーも、サーバーも、すでにターゲットとしているブラウザ以外のランタイムコストも一切不要です。各APIについて、一行の定義、最小限の動作サンプルコード、実際のユースケース、そして率直なサポート状況の注記を記載します。

重要なポイント

  • IntersectionObserverは、スクロールリスナーを使わずに要素がビューポートに入ったり出たりするタイミングを通知します。2019年3月よりすべてのブラウザでBaselineとして利用可能です。
  • ResizeObserverは、要素自身のcontent-boxまたはborder-boxのサイズ変化に反応します。これにより、windowのresizeイベントでは対応できないコンテナ対応コンポーネントの実装が可能になります。
  • AbortControllerはReactの競合状態(race condition)に対する標準的な解決策です。controller.signalfetchに渡し、エフェクトのクリーンアップでcontroller.abort()を呼び出すことで、古いレスポンスが新しいUIを上書きするのを防ぎます。
  • navigator.clipboard.writeText()は、document.execCommand('copy')というハック的な手法に代わるPromiseベースの実装ですが、セキュアコンテキスト(HTTPSまたはlocalhost)でのみ動作します。
  • 同一ドキュメント内のView Transitionsは2025年10月14日にBaseline Newly availableに到達しており、グレースフルデグラデーションに対応しているため、if (document.startViewTransition)によるガードを設けることで安全に本番環境へリリースできます。

IntersectionObserver:スクロールリスナーを置き換えるWeb API

IntersectionObserverは、対象要素がビューポート(または任意の祖先要素)に入ったり出たりするタイミングを非同期で通知します。これにより、スクロールリスナーを一切使わずに、遅延読み込み、無限スクロール、表示状態のアナリティクス計測を実装できます。2019年3月よりすべてのブラウザでBaselineとして利用可能です。スクロールリスナーはメインスレッド上で毎秒数十回発火し、ジオメトリの計算も自前で行う必要があります。一方、このオブザーバーはその処理をクリティカルパスの外でバッチ処理し、可視状態が実際に変化したときにのみコールバックを呼び出します。

const observer = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src; // 表示時のみ読み込む
      observer.unobserve(entry.target);
    }
  }
});

document.querySelectorAll('img[data-src]').forEach((img) => observer.observe(img));

このコードは、行がスクロールしてビューポートに入ったタイミングで画像を遅延読み込みします。同じisIntersectingチェックが、無限スクロールのセンチネルや「閲覧済み」アナリティクスにも活用できます。長いフィードのセッションリプレイを見ると、その効果は一目瞭然です。画面外の画像やトラッキングイベントは、ページ読み込み時にまとめて処理されるのではなく、各行がビューポートに入ったときにのみ処理されます。ほとんどのケースでreact-intersection-observerや手書きのスクロール計算を不要にします。(注:trackVisibilityオプションはまだ実験的な機能であり、Baselineではありません。isIntersectingの使用にとどめることを推奨します。)

ResizeObserver:要素レベルのリサイズ検知

コンポーネントが自身のサイズに応じて反応する必要がある場合は、windowのresizeイベントではなくResizeObserverを使用してください。ResizeObserverは要素レベルのcontent-boxおよびborder-boxの変化を通知するため、コンテナ対応コンポーネントの実装が可能になります。window.resizeハンドラーはビューポートが変化したことを伝えるだけで、サイドバーが開いたことでパネルが縮小したといった情報は一切提供しません。

const ro = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { width } = entry.contentRect;
    entry.target.classList.toggle('is-narrow', width < 400);
  }
});

ro.observe(document.querySelector('.card'));

このコードでは、カードが自身の幅に基づいてコンパクトなレイアウトに切り替わります。これはJavaScriptで実装するコンテナクエリです。現行ブラウザ全体でBaselineとして利用可能であり、element-resize-detectorのようなライブラリを不要にします。コンソールにResizeObserver loopという警告が表示された場合、ほとんどのケースでは無害であり、コールバック内でサイズを変更したことを意味します。

AbortController:fetchのキャンセルと競合状態の解消

AbortControllerはReactの競合状態(race condition)に対する標準的な解決策です。controller.signalfetchに渡し、エフェクトのクリーンアップでcontroller.abort()を呼び出すことで、高速な再ナビゲーション後に古いレスポンスが新しいUIを上書きすることを完全に防げます。AbortSignalは2018年4月よりすべてのブラウザで利用可能です。

useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/results?q=${query}`, { signal: controller.signal })
    .then((res) => res.json())
    .then(setResults)
    .catch((err) => {
      if (err.name !== 'AbortError') throw err;
    });
  return () => controller.abort(); // アンマウント時または新しいクエリ発行時にキャンセル
}, [query]);

このコードが解消する典型的なバグは、セッションリプレイで鮮明に確認できます。ユーザーが素早くタイピングした際に、先に送信した遅いリクエストのレスポンスが後から返ってきて、現在の画面に古いデータが上書きされてしまうというものです。クリーンアップ時に処理中のリクエストをキャンセルすることで、この問題を根本的に防ぎます。タイムアウトのみが必要な場合は、手動でコントローラーを作成する必要はありません。AbortSignal.timeout()は2024年4月よりすべてのブラウザで利用可能で、TimeoutErrorをスローしてリクエストをキャンセルします。一方、ユーザー操作によるキャンセルはAbortErrorをスローするため、一つのcatchブロックで両者を区別できます。

fetch(url, { signal: AbortSignal.timeout(5000) });

これにより、isMountedフラグやaxios.CancelTokenが不要になります。

Clipboard API:execCommandハックを使わないクリップボードコピー

navigator.clipboard.writeText()は、document.execCommand('copy')というハック的な手法に代わる、モダンなPromiseベースの実装です。ただし、セキュアコンテキスト(HTTPSまたはlocalhost)でのみ動作する点に注意してください。

copyBtn.addEventListener('click', async () => {
  await navigator.clipboard.writeText(snippet);
  copyBtn.textContent = 'Copied!';
});

これは、あらゆるドキュメントサイトに実装されている「コードスニペットをコピー」ボタンそのものです。書き込み操作はBaselineとして安定しています。一方、読み取り操作はベストエフォートです。readText()はクリップボードにテキストがない場合に空文字列で解決し、Firefoxでは書き込みよりも厳しい制限が課されています。そのため、コピー操作が信頼できる実装方法であり、貼り付け読み取りはプログレッシブエンハンスメントとして扱うのが最善です。clipboard.jsを不要にします。

View Transitions API:DOMの状態変化をネイティブにアニメーション化

View Transitions APIは、document.startViewTransition(() => updateDOM())を使ってDOMの状態変化をアニメーション化します。グレースフルデグラデーションに対応しているため(非対応ブラウザではアニメーションなしでDOMが更新される)、if (document.startViewTransition)によるガードを設けることで今すぐ本番環境へリリースできます。同一ドキュメント内のトランジションは2025年10月14日にBaseline Newly availableに到達し、Chrome 111以降、Edge 111以降、Firefox 144以降、Safari 18以降で動作します。

function updatePage(newContent) {
  if (!document.startViewTransition) {
    render(newContent); // フォールバック:即時更新、アニメーションなし
    return;
  }
  document.startViewTransition(() => render(newContent));
}

タブの切り替え、リストから詳細画面へのナビゲーション、フィルターの変更など、これまでFramer Motionを導入していたようなクロスフェードや共有要素のモーフィングアニメーションに活用できます。startViewTransition()のガードにより、非対応ブラウザでは変更が即座に適用されます。なお、ドキュメント間のトランジションはより限定的で、Chrome 126以降、Edge 126以降、Safari 18.2以降でのみ対応しており、Firefoxはまだ未対応です。両者を混同しないよう注意してください。

ボーナス:structuredCloneとfetchの注意点

次のタスクでこれらのAPIを一つ試して、置き換えられる依存関係を削除してみましょう。スクロールリスナーをIntersectionObserverに置き換えるか、検索のfetchをAbortControllerでラップするかのどちらかから始めてみてください。もう一つ、手元に置いておく価値のある機能としてstructuredClone()があります。JSON.parse(JSON.stringify(...))のような迂回処理を使わずにオブジェクトをディープコピーでき、Date、Map、Set、型付き配列も正しく保持されます。ただし、関数やDOMノードに対してはDataCloneErrorをスローするため、ライブオブジェクトではなくデータ専用の用途に限られます(2022年3月よりBaselineとして利用可能)。また、fetchを基本として使用している場合は、そのPromiseがHTTPの404や500ではrejectされず、ネットワーク障害時のみrejectされることを忘れずに確認してください。response.okを自前でチェックする必要があります。

よくある質問

IntersectionObserverとResizeObserverの違いは何ですか?

IntersectionObserverは、要素がビューポートや祖先要素に入ったり出たりするタイミングを通知するため、遅延読み込み、無限スクロール、表示状態のアナリティクス計測に活用されます。ResizeObserverは、要素自身のcontent-boxまたはborder-boxのサイズ変化を通知するため、コンテナ対応レイアウトの実装に使用されます。IntersectionObserverは「この要素は表示されているか」という問いに答え、ResizeObserverは「この要素のサイズはどれくらいか」という問いに答えます。目的が異なり、どちらもビューポートレベルの役割を担うwindow resizeイベントの代替にはなりません。

fetchリクエストのキャンセルにはAbortSignal.timeout()と手動のAbortControllerのどちらを使うべきですか?

固定のタイムアウトのみが必要な場合はAbortSignal.timeout()を使用してください。指定したミリ秒後にTimeoutErrorで自動的にキャンセルされ、コントローラー変数も不要です。一方、アンマウント時や再ナビゲーション時のReactエフェクトのクリーンアップなど、自分でキャンセルのタイミングを制御する必要がある場合は、手動のAbortControllerを使用してください。この場合はcontroller.abort()を自分で呼び出します。両者がスローするエラーはそれぞれTimeoutErrorとAbortErrorと異なるため、一つのcatchブロックで区別することができます。

ローカル開発環境でnavigator.clipboard.writeText()が失敗するのはなぜですか?

navigator.clipboard.writeText()はセキュアコンテキスト、つまりHTTPSまたはlocalhostでのみ動作します。ローカルIPアドレスやlocalhost以外のホスト名など、プレーンなHTTPアドレスで開発サーバーにアクセスした場合、ブラウザはクリップボードへの書き込みをブロックし、Promiseはrejectされます。localhostを使用するか、HTTPSで提供するようにしてください。readText()による読み取りはさらに制限が厳しく、特にFirefoxでは顕著です。そのため、書き込みを信頼できる実装方法として、貼り付け読み取りはプログレッシブエンハンスメントとして扱うことを推奨します。

一部のブラウザが対応していない場合でも、View Transitions APIは安全に使用できますか?

はい、グレースフルデグラデーションに対応しているため安全です。呼び出す前にif (document.startViewTransition)でガードしてください。非対応ブラウザではDOMの更新は実行されますが、アニメーションなしで即座に適用されます。同一ドキュメント内のトランジションは2025年10月14日にBaseline Newly availableに到達し、Chrome 111以降、Edge 111以降、Firefox 144以降、Safari 18以降で動作します。ドキュメント間のトランジションはより限定的で、Chrome 126以降、Edge 126以降、Safari 18.2以降のみ対応しており、Firefoxはまだ未対応です。

Open-source session replay

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.