スクロールが終了するまで処理を遅らせる

ウェブ機能: scrollend

ウェブでのスクロールは、スムーズでレスポンシブである必要があります。スクロール中に、レイアウトの再計算、アナリティクス データのビーコン トラッキング、動的な DOM の更新などの重いタスクを実行すると、メインスレッドが飽和状態になり、フレーム落ちやレイアウト スラッシングが発生する可能性があります。

これまで、デベロッパーは setTimeout() を使用して scroll イベントをデバウンスし、スクロールがいつ終了するかを推測していました。しかし、これらのデバウンス関数は信頼性が低いことで知られています。ユーザーがまだスクロールしている間にトリガーされることがあります。

scrollend イベントは、信頼性が高く、パフォーマンスを重視したソリューションを提供します。ブラウザは、スクロールが停止し、すべての遷移が完了し、タッチ操作がリリースされたときに、scrollend イベントを発生させます。

実装方法

遅延処理パターンを実装するには:

  1. スクロール可能なコンテナを設定する: overflow: auto または overflow: scroll を使用してコンテナを作成します。
  2. scroll イベントをリッスンする: このリスナーは、基本的な動的指標または情報レイアウトのスタイル設定にのみ使用します。ここで負荷の高い処理を実行しないでください。
  3. scrollend イベントをリッスンする: スクロール コンテナまたはドキュメント自体に scrollend コールバックを登録します。
  4. コールバックで負荷の高い処理を実行する: 動的コンテンツの取得や包括的な DOM レイアウトのトリガーを安全に行うことができます。

コード例

.scroll-container {
  height: 300px;
  overflow-y: auto;
}
const scroller = document.querySelector('.scroll-container');

// 1. Informative feedback during scroll
scroller.addEventListener('scroll', () => {
  // Avoid dynamic heavier data updates here
  console.log('Scrolling dynamically... updates deferred');
});

// 2. Safe callback when scrolling rests
scroller.addEventListener('scrollend', () => {
  // Run layout recalculations or analytical beacons updates here
  const currentVisibleSection = findMostVisibleSection(scroller);
  fetchAdditionalData(currentVisibleSection);
});

戦略的な実装とベスト プラクティス

  • レイアウト データのビーコンを送信する場合や、新しいコンテンツ レイアウトを動的に取得する場合は、デバウンスされた scroll イベントではなく、scrollend を使用します。
  • 推奨: カルーセルやお客様の声ギャラリーのスライドを作成する場合は、scrollSnapChange または scrollSnapChanging のスナップ インタラクションと組み合わせることを検討してください。
  • レイアウトに依存する動的な更新を、動的なビジュアル スクロール コールバックにバンドルしないでください。
  • 推奨事項: ビジュアル ビューポートのズームとスクロールによって scrollend イベントが正しくトリガーされることを考慮します。

代替戦略

サポートされていないブラウザの場合は、setTimeout を使用してデバウンスされた scroll イベントにフォールバックし、カスタム scrollend イベントをディスパッチします。

function initializeDemo() {
  const scroller = document.querySelector('#scroller');
  scroller.addEventListener('scrollend', () => {
    // Safe execution
  });
}

if ('onscrollend' in window) {
  initializeDemo();
} else {
  initializeDemo();
  const scroller = document.querySelector('#scroller');
  scroller.addEventListener('scroll', () => {
    clearTimeout(window.scrollendtimer);
    window.scrollendtimer = setTimeout(() => {
      scroller.dispatchEvent(new CustomEvent('scrollend'));
    }, 100);
  });
}