Close
Angular React Web Components Blazor Web Components
Open Source

Virtual Scroll コンポーネント

Ignite UI for Web Components Virtual Scroll は、ビューポート内のアイテムと設定可能なバッファーのみを DOM 内に保持することで、大量のリストをレンダリングするコンポーネントです。スクロールバーは常にコレクション全体の範囲を表すため、10 万件のアイテムを持つ仮想リストでも通常のリストと同じようにスクロールできます。

ライブ デモ

構造

Web Components Virtual Scroll は、表示されているアイテムと設定可能なバッファーをレンダリングし、そのトラックはコレクション全体のスクロール範囲を保持します。

Web Components Virtual Scroll anatomy: host, track, content element, item wrappers, and over-scan buffers
1. ホスト: スクロール コンテナーです。固定の高さ (水平の場合は幅) によって、表示されるアイテムの数が決まります。
2. トラック: コレクション全体の推定される長さに合わせてサイズが設定されるスペーサーです。これにより、スクロールバーがすべてのアイテムに及びます。
3. コンテンツ要素: レンダリングされたアイテムのみを保持します。ビューポートの上にある、最初にレンダリングされたバッファー アイテムから始まり、そのサイズはレンダリングされたアイテムによって決まります。
4. アイテム ラッパー: レンダリングされたアイテムごとに 1 つ存在し、アイテム テンプレートをホストします。サイズが測定されるのはこのボックスです。
5. オーバー スキャン バッファー: ビューポートの各端を超えてレンダリングされる overScan 個のアイテム (デフォルトは 2) です。
igc-virtual-scroll                            — scrollable viewport
└── [part="virtualization-track"]             — provides the collection's scroll range
    └── [part="virtualization-content"]       — positions the rendered window
        └── div[data-vs-index]                — one wrapper per rendered item; hosts the item template

作業の開始

作業の開始 のトピックに従って Ignite UI for Web Components をセットアップし、VirtualScroll を登録して、アイテム テンプレートとデータを指定します。

import { defineComponents, IgcVirtualScrollComponent } from 'igniteui-webcomponents';
import type { VirtualScrollItemContext } from 'igniteui-webcomponents';
import { html } from 'lit';

defineComponents(IgcVirtualScrollComponent);

const virtualScroll = document.querySelector('igc-virtual-scroll') as IgcVirtualScrollComponent<Item>;
virtualScroll.itemTemplate = (ctx: VirtualScrollItemContext<Item>) =>
    html`<div class="row">${ctx.index}: ${ctx.value.name}</div>`;
virtualScroll.data = Array.from({ length: 100_000 }, (_, i) => ({ name: `Item ${i}` }));
<igc-virtual-scroll style="height: 400px"></igc-virtual-scroll>

Virtual Scroll のホストには、垂直スクロールの場合は固定の高さ、水平スクロールの場合は固定の幅が必要です。コンテンツに合わせてサイズが拡大するホストはすべてのアイテムをレンダリングするため、リストは仮想化されません。

前提条件とバージョン互換性

要件 値
パッケージ igniteui-webcomponents (MIT)
コンポーネントが最初にリリースされたバージョン 7.3.0
アイテム テンプレート Lit の html タグ。lit は igniteui-webcomponents の依存関係ですが、直接インポートする場合は自身の依存関係にも追加してください。

使用方法

アイテム テンプレート

Virtual Scroll のアイテム テンプレートは、アイテムとコレクション全体におけるその位置を受け取ります。位置に依存するコンテンツ (交互のスタイルや aria-posinset、aria-setsize など) には、インデックスと合計件数を使用します。

itemTemplate に、Lit テンプレートを返す関数を設定します。この関数は、value (アイテム)、index、count、isFirst、isLast を持つ VirtualScrollItemContext を受け取ります。アイテム テンプレートがない場合、コンポーネントは何もレンダリングしません。

virtualScroll.itemTemplate = (ctx: VirtualScrollItemContext<Employee>) => html`
    <igc-list-item aria-posinset=${ctx.index + 1} aria-setsize=${ctx.count}>
        <igc-avatar slot="start" shape="circle" initials=${ctx.value.initials}></igc-avatar>
        <span slot="title">${ctx.value.name}</span>
        <span slot="subtitle">${ctx.value.email}</span>
    </igc-list-item>
`;

データ

Virtual Scroll の data コレクションは参照によって比較されます。リストを更新するには新しい配列を割り当てる必要があります。push などでバインドされた配列をその場で変更しても更新されません。

virtualScroll.data = [...virtualScroll.data, newEmployee];

data が変更されると、コンポーネントは最初に変更されたインデックスより前のアイテムの測定済みサイズを保持し、それ以降のアイテムはレンダリング時に再度測定します。追加操作ではすべての既存の測定値が保持されますが、置換、フィルタリング、ソートでは最初に変更されたアイテム以降の測定値が破棄されます。

アイテムは、そのキーがレンダリングされたウィンドウ内にある間、同じ要素を保持します。keyFunction を指定しない場合はインデックスがキーになるため、ソート、挿入、削除の後もそのインデックスの要素はそのまま残り、新しいアイテムを表示します。アイテムが data 内で移動する場合は、keyFunction から安定した ID を返してください。

virtualScroll.keyFunction = (employee) => employee.id;

推定アイテム サイズ

Virtual Scroll の estimatedItemSize は、アイテムがレンダリングされて測定されるまでのピクセル単位のサイズです (デフォルトは 50)。アイテムは異なるサイズを持つことができ、測定されたサイズがそれぞれ推定値を置き換えます。

最初のレンダリングが実際の内容に近くなるよう、推定値はアイテムの平均サイズに近い値に設定してください。アイテムが測定されると、まだ測定されていないアイテムの推定値は測定済みの平均サイズに置き換えられるため、推定値がずれていてもスクロールバーと scrollToIndex は自動的に補正されます。

<igc-virtual-scroll estimated-item-size="80" style="height: 480px"></igc-virtual-scroll>

アイテムはボーダー ボックスで測定されるため、マージンはアイテムのサイズに含まれません。マージンの代わりに、パディング、またはアイテム内の gap を使用してアイテム間の間隔を設定してください。

方向

Virtual Scroll の orientation は、スクロール軸を vertical (デフォルト) または horizontal に設定します。水平リストでは、各アイテムに幅を、ホストに高さを設定してください。右から左のコンテキストでは、水平スクロールとアイテムの配置が反転します。

<igc-virtual-scroll orientation="horizontal" estimated-item-size="220" style="height: 200px"></igc-virtual-scroll>

オーバー スキャン

Virtual Scroll の overScan は、ビューポートの各端を超えてレンダリングされる追加アイテムの数です (デフォルトは 2)。値を大きくすると、高速スクロール中の空白領域が減りますが、レンダリングする要素が増えます。

<igc-virtual-scroll over-scan="6" style="height: 400px"></igc-virtual-scroll>

インデックスへのスクロール

Virtual Scroll の scrollToIndex メソッドは、アイテムが表示される位置までスクロールします。オプションはネイティブの scrollIntoView と同じで、block (start、center、end、または nearest)、水平リスト用の inline、behavior (auto または smooth) を指定できます。まだレンダリングされていないアイテムには推定サイズしかないため、コンポーネントは到達した位置のアイテムを測定して位置を補正します。返されるプロミスは最終的な位置が確定すると解決されます。

await virtualScroll.scrollToIndex(index, { block: 'center' });

block: 'nearest' を指定した場合、アイテムがすでに完全に表示されているときは位置が変更されません。コレクションの範囲外のインデックスは、最初または最後のアイテムに丸められます。

無限スクロール

Virtual Scroll の igcDataRequest イベントは、リモート データからの追加専用の読み込みをサポートします。これは、レンダリングされたウィンドウが data の末尾に近づいたときや、読み込まれたアイテムが最初のレンダリングでビューポートを満たさない場合に発生します。要求されたアイテムを新しい配列として追加します。

virtualScroll.addEventListener('igcDataRequest', (event: CustomEvent<VirtualScrollDataRequest>) => {
    const { startIndex, count } = event.detail;
    fetchEmployees(startIndex, count).then(page => {
        virtualScroll.data = [...virtualScroll.data, ...page];
    });
});

一度に保留できるデータ要求は 1 つだけで、次の要求は data が次に変更された後に発行されます。data が空の場合は要求が発行されないため、最初のページは自分で読み込む必要があります。ソースにこれ以上アイテムがない場合は、追加を停止するだけでかまいません。コンポーネントが同じ開始インデックスを再度要求することはありません。

レイアウト完了

Virtual Scroll の layoutComplete プロパティは、現在のレンダリング、それによってトリガーされる測定、そしてそれらがスケジュールするレンダリングが完了したときに解決されるプロミスです。data の変更、スクロール、リサイズの後にレンダリングされたアイテムを読み取る前に、これを待機してください。

virtualScroll.data = await fetchEmployees();
await virtualScroll.layoutComplete;

使用すべき場合と使用すべきでない場合

Virtual Scroll は通常、長いリストのスクロール コンテナーとして機能し、ビューポート内のアイテムと小さなバッファーのみを DOM に保持します。一度にレンダリングできる短いリストには使用しないでください。また、アイテム テンプレートを作成する際は、各アイテムの状態を要素ではなくデータに保持してください。アイテムの要素は再利用されるため、テンプレートがバインドしていない DOM の状態は、その要素を受け取ったアイテムに表示されます。

Web Components Virtual Scroll showing a list of 100,000 employees
使用すべき

ディレクトリ、フィード、ログ、横一列に並んだカードなど、一度にレンダリングするには大きすぎる長いリストに対して Virtual Scroll を使用します。これには、スクロール中にリモート データを読み込むリストも含まれます。

Web Components Virtual Scroll used for a list of only five employees
使用すべきでない

短いリストは List で直接レンダリングします。列、ソート、フィルタリングを伴う表形式のデータには Web Components Data Grid を使用します。仮想化せずに少数のリッチなアイテムを表示するには Card を使用します。

プロパティ

名前 型 デフォルト 説明
data T[] [] 仮想化するコレクション。参照によって比較されます。プロパティのみ。
orientation 'vertical' | 'horizontal' 'vertical' スクロール軸。属性: orientation。
overScan number 2 ビューポートの各端を超えてレンダリングされる追加アイテムの数。属性: over-scan。
estimatedItemSize number 50 アイテムが測定されるまでのピクセル単位のサイズ。0 以下の値の場合は 50 が使用されます。属性: estimated-item-size。
itemTemplate VirtualScrollItemTemplate<T> | null null 各アイテムをレンダリングする関数。プロパティのみ。
keyFunction VirtualScrollKeyFunction<T> | null null アイテムのキーを返します。これにより、アイテムが data 内で移動しても同じ要素が保持されます。指定しない場合はインデックスがキーになります。プロパティのみ。
layoutComplete Promise<void> (読み取り専用) — レンダリングとアイテムの測定が完了すると解決されます。

メソッド

名前 戻り値 説明
scrollToIndex(index: number, options?: ScrollIntoViewOptions) Promise<void> index のアイテムをビューにスクロールし、最終的なスクロール位置が確定すると解決されます。

イベント

名前 詳細 説明
igcStateChange VirtualScrollState レンダリングされたウィンドウが変更されたときに発生します: startIndex、endIndex、viewportSize、totalSize。
igcDataRequest VirtualScrollDataRequest レンダリングされたウィンドウが data の末尾に近づいたときに発生します: startIndex、count。

スタイル設定

Web Components Virtual Scroll には独自のテーマはありません。ビューポートのレイアウトのみを行い、レンダリングされたアイテムはアイテム テンプレート内の要素やコンポーネントからスタイルを継承します。

コンポーネントはライト DOM 内にレンダリングされるため、通常のセレクターでレンダリングされたアイテムに到達できます。デフォルトのスタイルはホストに 18.75rem の高さを与えます。ビューポートのサイズを変更するには、この高さを上書きしてください。

igc-virtual-scroll.employees {
    height: 480px;
}

igc-virtual-scroll.employees [data-vs-index]:nth-child(even) {
    background: var(--ig-gray-100);
}

アクセシビリティ

Web Components Virtual Scroll は、レンダリングされたウィンドウのみを DOM に保持するため、アイテム テンプレートはコレクション全体の中での各アイテムの位置を公開する必要があります。

キーボード インタラクション

Web Components Virtual Scroll はキー ハンドラーを追加しません。ホストはネイティブのスクロール コンテナーであり、フォーカスされたスクロール コンテナーはブラウザー標準のキー操作でスクロールします。

キー アクション
上矢印 / 下矢印 垂直リストをスクロールします。
左矢印 / 右矢印 水平リストをスクロールします。
Page Up / Page Down ビューポート 1 つ分ほどスクロールします。
Home / End コレクションの先頭または末尾にスクロールします。

ホストには tabindex がありません。フォーカス可能なコンテンツがないスクロール コンテナーがフォーカスを受け取れるかどうかはブラウザーによって異なるため、アイテムにフォーカス可能な要素が含まれない場合は、ホストに tabindex="0" を設定してください。アイテム内のフォーカスは、そのアイテムがレンダリングされたウィンドウから外れると保持されません。外れる前に意図的にフォーカスを移動してください。

スクリーン リーダー / ARIA

  • トラック、コンテンツ要素、アイテム ラッパーには role="presentation" が設定されています。ホストにはロールがありません。igc-list のようにリストのロールを持つ要素の中に配置するか、アイテムが role="listitem" をレンダリングする場合はホストに role="list" を設定してください。
  • index と count のコンテキスト プロパティを aria-posinset と aria-setsize にマップします。
  • フォーカス可能なホストには、role="list" のようにアクセシブルな名前をサポートするロールを設定し、aria-label または aria-labelledby で名前を付けてください。

アクセシビリティ準拠

インフラジスティックスは、Ignite UI for Web Components が対象とするアクセシビリティ標準を アクセシビリティ準拠 トピックで文書化しています。このトピックは Virtual Scroll に対する準拠の主張を行うものではありません。表にはコンポーネントが提供する内容が記載されており、その後のリストにはアプリケーションが追加する必要がある内容が記載されています。

基準 コンポーネントが要件をサポートする方法
1.3.1 情報及び関係性 ラッパーには role="presentation" が設定されているため、リスト構造はホストとアイテム テンプレートから提供され、aria-posinset と aria-setsize で各アイテムの位置を公開できます。
2.1.1 キーボード ホストはネイティブのスクロール コンテナーであり、フォーカスを得るとキーボードでスクロールできます。キーボードでホストに到達できるかどうかはアプリケーションに依存します。以下のリストを参照してください。

ユーザー側の責任:

  • アイテムにフォーカス可能な要素が含まれない場合は、tabindex="0" を設定してホストをキーボードで到達可能にし、アクセシブルな名前を付けてください。
  • アイテム テンプレートから aria-posinset と aria-setsize でアイテムの位置を公開してください。
  • アイテム テンプレートに適したリスト セマンティクスを提供してください (スクリーン リーダー / ARIA を参照)。
  • 選択などのアプリケーションの状態は、レンダリングされたアイテム要素ではなくデータ内に保持してください。

トラブルシューティング

Virtual Scroll がアイテムをレンダリングしないのはなぜですか?

ホストにスクロール軸方向のサイズがない、アイテム テンプレートがない、または data が空です。ホストに固定の高さ (垂直) または幅 (水平) を設定し、アイテム テンプレートを設定して、バインドされたコレクションを確認してください。

アイテムを追加してもリストが更新されないのはなぜですか?

Virtual Scroll は data を参照によって比較するため、その場での変更は検出されません。[...items, newItem] のように新しい配列を割り当ててください。

スクロール中にスクロールバーのサイズが変わるのはなぜですか?

まだレンダリングされていないアイテムは estimatedItemSize を使用しており、アイテムが測定されるにつれて合計サイズが補正されます。

その後、まだ測定されていないアイテムの推定値は測定済みの平均サイズに置き換えられるため、スクロールするにつれて補正が収束します。最初のレンダリングが実際の内容に近くなるよう、estimatedItemSize はアイテムの平均サイズに近い値に設定してください。

リストの下の方でアイテムの位置がずれるのはなぜですか?

マージンはアイテムの測定済みサイズに含まれません。アイテムのマージンをパディングまたはアイテム内の gap に置き換えてください。

既知の制限

  • Web Components Virtual Scroll は単一の軸を仮想化します。行と列の両方を仮想化するにはグリッドが必要です。
  • ウィンドウの移動に伴ってアイテムの要素は再利用されるため、checked をバインドしていないチェックボックスなど、アイテム テンプレートがバインドしていない DOM の状態は、その要素を受け取ったアイテムに表示されます。アイテムの状態はすべてバインドし、ユーザーによる変更はアイテムに書き戻してください。

API リファレンス

依存関係

Web Components Virtual Scroll は他のコンポーネントへの依存関係を持たず、独自のテーマも必要としません。アイテム テンプレートのレンダリングには lit を使用し、アイテム テンプレート内のコンポーネントにはテーマ スタイルシートが必要です。

その他のリソース

関連コンポーネント

  • List - 短いリストや、仮想化されたリストのコンテナーとして List を使用します。
  • Data Grid - 列、ソート、フィルタリングを伴う表形式のデータには Data Grid を使用します。
  • Card - 少数のリッチなアイテムの表示や、水平方向の Virtual Scroll のアイテムとしてカードを使用します。

FAQ

Virtual Scroll はどれくらいの数のアイテムを処理できますか?

Web Components Virtual Scroll は、ビューポート内のアイテムとオーバー スキャン バッファーのみを DOM に保持するため、コレクションのサイズによってレンダリングされる要素の数は変わりません。コレクションの合計サイズがブラウザーのスクロール制限を超える場合、Virtual Scroll はコレクションをブラウザーがサポートするスクロール範囲にマッピングします。

Virtual Scroll のアイテムは同じサイズである必要がありますか?

Web Components Virtual Scroll のアイテムは、レンダリングされた時点でそれぞれが測定されるため、異なるサイズにすることができます。アイテムが測定される前にスクロールバーを正確に保つため、estimatedItemSize をアイテムの平均サイズに近い値に設定してください。

アイテムが測定されると、まだ測定されていないアイテムの推定値は測定済みの平均サイズに置き換えられます。

Virtual Scroll を特定のアイテムまでスクロールするにはどうすればよいですか?

Web Components Virtual Scroll の scrollToIndex メソッドを、アイテムのインデックスと、省略可能な block および behavior オプションを指定して呼び出します。このメソッドは、補正された位置が安定すると解決されるプロミスを返します。

ユーザーがスクロールしている間に Virtual Scroll にリモート データを読み込むにはどうすればよいですか?

Web Components Virtual Scroll は、igcDataRequest イベントを通じて追加専用の読み込みをサポートします。イベントを処理し、要求されたアイテムを含む新しい配列を data に割り当てます。