📜

仮想スクロールを実装する各手段の長所と短所

2024/10/12に公開

はじめに

ウェブアプリケーションで大量のデータを扱うために、膨大なリストやテーブルをスムーズに表示する手段として「仮想スクロール」があります。
仮想スクロールは画面に表示されていない要素や領域のレンダリングを省略することでパフォーマンスを最適化するだけでなく、 HTML 要素の大きさの限界を突破するためにも必要です。
仮想スクロールの実装方法は多岐にわたり、それぞれに長所と短所が存在します。
最適な手段を選択する参考になればと思います。

スクロールの検出

大きく分けて2つの手段があります。
表示領域の数倍の大きさの描画領域を確保しておいて onscroll イベントや Intersection Observer を使って再描画する方法と onwheel を使う方法に分かれます。

onscroll による実装🎧

単純で分かりやすく多くのブラウザで動きます。
pagedown キーやタッチスクリーンなどによるスクロールもサポートできます。
一方で頻繁にイベントが発火するため、処理が重いとフレームレートが低下します。

Intersection Observer による実装🔭

ブラウザが最適化した方法で要素の可視性を監視してくれます。
pagedown キーやタッチスクリーンなどによるスクロールもサポートできます。
一方で検知用の html 要素を別途挿入する必要があるかもしれません。
比較的新しい API なので古いブラウザではサポートされていない場合があり、追加で学習が必要になる可能性もあります。

onwheel による実装🖱️

ユーザーのスクロール操作を直接検知できます。
一方でタッチスクリーンやモバイルデバイスや pagedown キーなどによる操作に対応できません。
他の実装に比べてスクロール位置の正確な追跡が難しく、追加のロジックが必要になることがあります。

必要な要素のみを適切に描画

仮想スクロールでは画面内の要素のみをうまく描画する必要があります。

ダミー要素の配置による実装📦

表示領域の数倍の大きさの描画領域を確保するために、上部や下部にダミーのスペーサー要素を配置できます。
基本的な HTML と CSS で実装可能で、複雑なレイアウトが不要です。
各要素の大きさが描画するまで分からないような場合でも対応しやすいです。
一方で描画順序に制限がかかり上から順に要素をならべる必要があり、行や列を固定したい場合に z-index などの指定をすることになります。

position: absolute による実装📍

各要素を position: absolute で配置し、スクロール位置に応じて位置を計算して表示します。
要素の位置を自由に制御でき、複雑なレイアウトに柔軟に対応できます。
一方で各要素の位置を計算するためのロジックが複雑になります。

canvas/SVG による実装🎨

大量の要素を描画する際に canvas は高速なレンダリングが可能です。
特にグラフィカルな要素やカスタムな描画が必要な場合に適しています。
一方で個々の要素に対するクリックやホバーなどのイベント処理が難しくなります。
テキスト選択やコピーでの対応も別途実装が要ります。
アクセシビリティに問題が起きやすそうです。

CSS Grid による実装🧮

CSS Grid レイアウトを使用して、要素をグリッド上に配置します。
各要素に position: sticky をつけて固定された行や列を簡単に実装できます。
一方で古いブラウザではサポートされていない場合があります。

まとめ

正直なところページャーなどで仮想スクロールは避けた方が楽だと思います。
仮に仮想スクロールを使うとしても広く使われているライブラリを使う方が良いです。
とはいえそういったライブラリは抽象度が高く特殊なユースケースへの対応が難しいこともあります。

個人的には onscroll と CSS Grid を使いつつ、必要なら一部の要素を position: absolute で描画するような実装がベストに感じています。
実装に使えそうな関数をライブラリにして公開しているので使ってみてください。
@ytoune/virtualized - npm

GitHubで編集を提案

Discussion