Skip to main content
Version: v6

ion-virtual-scroll

note

This component has been deprecated in favor of using virtual scrolling libraries provided by each JavaScript Framework. See below for alternatives.

Virtual Scroll displays a virtual, "infinite" list. An array of records is passed to the virtual scroll containing the data to create templates for. The template created for each record, referred to as a cell, can consist of items, headers, and footers. For performance reasons, not every record in the list is rendered at once; instead a small subset of records (enough to fill the viewport) are rendered and reused as the user scrolls.

このガイドでは、各フレームワークの統合に推奨される仮想スクロールパッケージと、Ionic Angularの非推奨コンポーネントである ion-virtual-scroll のドキュメントについて説明します。フレームワーク固有のソリューションの利用をお勧めしますが、まだそのコンポーネントを使用している開発者のために ion-virtual-scroll のドキュメントを以下に掲載します。

Angular

Ionic Angularの仮想スクロールのオプションについては、Angular仮想スクロールガイドを参照してください。

React

Ionic Reactでの仮想スクロールのオプションについては、React仮想スクロールガイドを参照してください。

Vue

Ionic Vueでの仮想スクロールのオプションについては、Vue仮想スクロールガイドを参照してください。


以下のドキュメントは ion-virtual-scroll コンポーネントに適用されます。

Approximate Widths and Heights

If the height of items in the virtual scroll are not close to the default size of 40px, it is extremely important to provide a value for the approxItemHeight property. An exact pixel-perfect size is not necessary, but without an estimate the virtual scroll will not render correctly.

The approximate width and height of each template is used to help determine how many cells should be created, and to help calculate the height of the scrollable area. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is only used to help calculate initial dimensions.

It's also important to know that Ionic's default item sizes have slightly different heights between platforms, which is perfectly fine.

Images Within Virtual Scroll

HTTP requests, image decoding, and image rendering can cause jank while scrolling. In order to better control images, Ionic provides <ion-img> to manage HTTP requests and image rendering. While scrolling through items quickly, <ion-img> knows when and when not to make requests, when and when not to render images, and only loads the images that are viewable after scrolling. Read more about ion-img.

It's also important for app developers to ensure image sizes are locked in, and after images have fully loaded they do not change size and affect any other element sizes. Simply put, to ensure rendering bugs are not introduced, it's vital that elements within a virtual item does not dynamically change.

For virtual scrolling, the natural effects of the <img> are not desirable features. We recommend using the <ion-img> component over the native <img> element because when an <img> element is added to the DOM, it immediately makes a HTTP request for the image file. Additionally, <img> renders whenever it wants which could be while the user is scrolling. However, <ion-img> is governed by the containing ion-content and does not render images while scrolling quickly.

Virtual Scroll Performance Tips

iOS Cordova WKWebView

When deploying to iOS with Cordova, it's highly recommended to use the WKWebView plugin in order to take advantage of iOS's higher performing webview. Additionally, WKWebView is superior at scrolling efficiently in comparison to the older UIWebView.

Lock in element dimensions and locations

In order for virtual scroll to efficiently size and locate every item, it's very important every element within each virtual item does not dynamically change its dimensions or location. The best way to ensure size and location does not change, it's recommended each virtual item has locked in its size via CSS.

Use ion-img for images

When including images within Virtual Scroll, be sure to use ion-img rather than the standard <img> HTML element. With ion-img, images are lazy loaded so only the viewable ones are rendered, and HTTP requests are efficiently controlled while scrolling.

Set Approximate Widths and Heights

As mentioned above, all elements should lock in their dimensions. However, virtual scroll isn't aware of the dimensions until after they have been rendered. For the initial render, virtual scroll still needs to set how many items should be built. With "approx" property inputs, such as approxItemHeight, we're able to give virtual scroll an approximate size, therefore allowing virtual scroll to decide how many items should be created.

Changing dataset should use trackBy

It is possible for the identities of elements in the iterator to change while the data does not. This can happen, for example, if the iterator produced from an RPC to the server, and that RPC is re-run. Even if the "data" hasn't changed, the second response will produce objects with different identities, and Ionic will tear down the entire DOM and rebuild it. This is an expensive operation and should be avoided if possible.

Each virtual item must stay extremely efficient, but one way to really kill its performance is to perform any DOM operations within section header and footer functions. These functions are called for every record in the dataset, so please make sure they're performant.

使い方

<ion-content>
<ion-virtual-scroll [items]="items" approxItemHeight="320px">
<ion-card *virtualItem="let item; let itemBounds = bounds;">
<div>
<ion-img [src]="item.imgSrc" [height]="item.imgHeight" [alt]="item.name"></ion-img>
</div>
<ion-card-header>
<ion-card-title>{{ item.name }}</ion-card-title>
</ion-card-header>
<ion-card-content>{{ item.content }}</ion-card-content>
</ion-card>
</ion-virtual-scroll>
</ion-content>
export class VirtualScrollPageComponent {
items: any[] = [];

constructor() {
for (let i = 0; i < 1000; i++) {
this.items.push({
name: i + ' - ' + images[rotateImg],
imgSrc: getImgSrc(),
avatarSrc: getImgSrc(),
imgHeight: Math.floor(Math.random() * 50 + 150),
content: lorem.substring(0, Math.random() * (lorem.length - 100) + 100)
});

rotateImg++;
if (rotateImg === images.length) {
rotateImg = 0;
}
}
}
}

const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, seddo eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

const images = [
'bandit',
'batmobile',
'blues-brothers',
'bueller',
'delorean',
'eleanor',
'general-lee',
'ghostbusters',
'knight-rider',
'mirth-mobile'
];

function getImgSrc() {
const src = 'https://dummyimage.com/600x400/${Math.round( Math.random() * 99999)}/fff.png';
rotateImg++;
if (rotateImg === images.length) {
rotateImg = 0;
}
return src;
}

let rotateImg = 0;

Basic

The array of records should be passed to the items property on the ion-virtual-scroll element. The data given to the items property must be an array. An item template with the *virtualItem property is required in the ion-virtual-scroll. The *virtualItem property can be added to any element.

<ion-virtual-scroll [items]="items">
<ion-item *virtualItem="let item">
{{ item }}
</ion-item>
</ion-virtual-scroll>

Section Headers and Footers

Section headers and footers are optional. They can be dynamically created from developer-defined functions. For example, a large list of contacts usually has a divider for each letter in the alphabet. Developers provide their own custom function to be called on each record. The logic in the custom function should determine whether to create the section template and what data to provide to the template. The custom function should return null if a template shouldn't be created.

<ion-virtual-scroll [items]="items" [headerFn]="myHeaderFn">
<ion-item-divider *virtualHeader="let header">
{{ header }}
</ion-item-divider>
<ion-item *virtualItem="let item">
Item: {{ item }}
</ion-item>
</ion-virtual-scroll>

Below is an example of a custom function called on every record. It gets passed the individual record, the record's index number, and the entire array of records. In this example, after every 20 records a header will be inserted. So between the 19th and 20th records, between the 39th and 40th, and so on, a <ion-item-divider> will be created and the template's data will come from the function's returned data.

myHeaderFn(record, recordIndex, records) {
if (recordIndex % 20 === 0) {
return 'Header ' + recordIndex;
}
return null;
}

Custom Components

If a custom component is going to be used within Virtual Scroll, it's best to wrap it with a <div> to ensure the component is rendered correctly. Since each custom component's implementation and internals can be quite different, wrapping within a <div> is a safe way to make sure dimensions are measured correctly.

<ion-virtual-scroll [items]="items">
<div *virtualItem="let item">
<my-custom-item [item]="item">
{{ item }}
</my-custom-item>
</div>
</ion-virtual-scroll>

プロパティ

approxFooterHeight

Description各フッターテンプレートのセルのおおよその幅を指定します。この寸法は、初期化時に作成されるべきセルの数を決定したり、スクロール可能な領域の高さを計算したりするのに使用されます。この高さの値には px 単位しか使用できません。各セルの実際のレンダリングサイズはアプリのCSSから得られますが、この近似値はアイテムがレンダリングされる前に初期寸法を計算するために使用されることに注意してください。
Attributeapprox-footer-height
Typenumber
Default30

approxHeaderHeight

Description各ヘッダーテンプレートのセルのおおよその高さを表します。この寸法は、初期化時に作成されるべきセルの数を決定したり、スクロール可能な領域の高さを計算したりするのに使用されます。この高さの値には px 単位しか使用できません。各セルの実際のレンダリングサイズはアプリのCSSから得られますが、この近似値はアイテムがレンダリングされる前に初期寸法を計算するために使用されることに注意してください。
Attributeapprox-header-height
Typenumber
Default30

approxItemHeight

Description仮想アイテムの高さがデフォルトよりかなり大きくなる場合は、これを指定することが重要です。 各仮想アイテムテンプレートのセルのおおよその高さ。この寸法は、初期化時に作成されるべきセルの数を決定したり、スクロール可能な領域の高さを計算したりするのに使用されます。この高さの値には px 単位しか使用できません。各セルの実際のレンダリングサイズはアプリの CSS から得られるもので、この近似値はアイテムがレンダリングされる前に初期寸法を計算するのに役立つものであることに注意してください。
Attributeapprox-item-height
Typenumber
Default45

footerFn

Descriptionセクションフッターと与えられたテンプレート内で使用されるデータは、footerFnに関数を渡すことで動的に作成することができます。フッター関数内のロジックは、フッターテンプレートを使用するかどうか、またフッターテンプレートにどのようなデータを与えるかを決定することができます。フッターセルが作成されない場合は、関数は null を返さなければなりません。
Attributeundefined
Type((item: any, index: number, items: any[]) => string | null | undefined) | undefined
Defaultundefined

footerHeight

Description各項目のフッターをその高さ内にマッピングするオプション関数。
Attributeundefined
Type((item: any, index: number) => number) | undefined
Defaultundefined

headerFn

Descriptionセクションヘッダーとそのテンプレート内で使用されるデータは、headerFnに関数を渡すことによって動的に作成することができます。例えば、大きな連絡先リストでは、通常、アルファベットの各文字の間に仕切りがあります。アプリは独自のカスタム headerFn を提供することができ、データセット内の各レコードで呼び出される。ヘッダー関数内のロジックは、ヘッダーテンプレートを使用するかどうか、またヘッダーテンプレートにどのようなデータを与えるかを決定することができる。ヘッダーセルが作成されない場合は、この関数は null を返さなければなりません。
Attributeundefined
Type((item: any, index: number, items: any[]) => string | null | undefined) | undefined
Defaultundefined

headerHeight

Description各項目のヘッダを高さの範囲内でマッピングするオプション関数。
Attributeundefined
Type((item: any, index: number) => number) | undefined
Defaultundefined

itemHeight

Description各アイテムをその高さ内にマッピングするオプションの関数です。この関数が提供されると、ion-virtual-scrollによって重い最適化と高速パスが取られるようになり、大幅な性能向上が期待できる。 この関数は、DOMの読み込みをすべてスキップすることができ、Doingすることで大幅なパフォーマンスの向上につながります。
Attributeundefined
Type((item: any, index: number) => number) | undefined
Defaultundefined

items

Description仮想スクロール内のテンプレートを構築するためのデータです。このデータが変更された場合、仮想スクロール全体がリセットされることになります。
Attributeundefined
Typeany[] | undefined
Defaultundefined

nodeRender

Description注:Vanilla JS APIのみです。
Attributeundefined
Type((el: HTMLElement | null, cell: Cell, domIndex: number) => HTMLElement) | undefined
Defaultundefined

renderFooter

Description注:ステンシル用のJSX APIのみ。 フッターをレンダリングするためのrender関数を提供します。JSXのvirtual-domを返します。
Attributeundefined
Type((item: any, index: number) => any) | undefined
Defaultundefined

renderHeader

Description注:ステンシル用のJSX APIのみ。 レンダリングされるヘッダーのrender関数を提供します。JSXのvirtual-domを返します。
Attributeundefined
Type((item: any, index: number) => any) | undefined
Defaultundefined

renderItem

Description注:ステンシル用のJSX APIのみ。 レンダリングするアイテムのレンダー関数を用意します。JSXのvirtual-domを返します。
Attributeundefined
Type((item: any, index: number) => any) | undefined
Defaultundefined

イベント

No events available for this component.

メソッド

checkEnd

Descriptionこのメソッドは、アイテム配列の末尾をダーティとしてマークし、再レンダリングできるようにします。 これは次のように呼び出すのと同じです: ``js virtualScroll.checkRange(lastItemLen); ```。
SignaturecheckEnd() => Promise<void>

checkRange

Descriptionこのメソッドは、アイテムのサブセットをダーティとしてマークし、再レンダリングができるようにします。アイテムは、コンテンツまたはそのスタイルが変更されるたびに、ダーティとしてマークされる必要があります。 更新されるアイテムのサブセットは、オフセットと長さで指定することができます。
SignaturecheckRange(offset: number, len?: number) => Promise<void>

positionForItem

Description指定されたインデックスの位置にある仮想アイテムの位置を返します。
SignaturepositionForItem(index: number) => Promise<number>

CSS Shadow Parts

No CSS shadow parts available for this component.

CSSカスタムプロパティ

No CSS custom properties available for this component.

Slots

No slots available for this component.