Scrolling is one of the most common interactions with a mobile application, and it is extremely important that it feels right. Native applications have lists that scroll smoothly, they are responsive to touch and changes of directions, they accelerate and decelerate in a way that feels natural.
It’s important that we can capture this same feeling with web tech, but it can be pretty hard to do. When lists become long, the DOM (Document Object Model) becomes larger and larger and much more resource intensive to manipulate. The DOM is usually the bottleneck for a lot of performance issues. The frameworks that can handle this well feel just like a native application, and the ones that don’t stick out like a sore thumb.
Virtual Scrolling addresses this issue. In Ionic 1 this was called Collection Repeat, in Ionic 2 it is called Virtual Scroll, but it is a concept that is used widely in HTML5 mobile development. The basic idea is that we only create enough elements in the DOM to display the list data that is currently on screen, and we recycle those DOM elements to display new data as they scroll off screen. This diagram should help explain the concept:
NOTE: This is just my understanding of how the process works in general, not necessarily the exact way it is implemented in Ionic 2.
So, we have enough DOM elements to fill up the screen, plus a few more as a bit of a buffer. As elements scroll off screen they are recycled and used for new data that is about to come on screen. Without virtual scrolling it would look more like this:
Now all of the DOM elements that we will ever need for the list are created right away, which causes the DOM to become very bloated and slow. The example above wouldn’t be so bad, as we only have maybe 20 items or so, but when you start extending it out to 1000’s of items it starts to become a real problem.
This is why we use frameworks like Ionic, and why I always stress that it’s a really bad idea to just try and create your own user interface – complex performance improvements are handled right out of the box.
So let’s take a look at an actual example in Ionic 2.
Before we Get Started
Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 set up on your machine.
If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.
Generate a new Ionic 2 application
We’re going to generate a new tabs application so that we can create both a standard list and a virtual scroll list in different tabs.
Run the following command:
ionic start virtual-scroll tabs --v2
Create the Data
We need some data to display in our lists, so we’re going to auto generate a big data set using a for
loop.
Modify the constructors of both home.ts and about.ts to reflect the following:
items: any;
constructor() {
this.items = [];
for(let i = 0; i < 2000; i++){
let item = {
title: 'Title',
body: 'body',
avatarUrl: 'https://avatars.io/facebook/random'+i
};
this.items.push(item);
}
}
Create a Standard List
First we’re going to set up a standard list to display the data we created in the constructor.
Modify home.html to reflect the following:
<ion-header>
<ion-navbar>
<ion-title> Standard Scroll </ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-avatar item-left>
<ion-img [src]="item.avatarUrl"></ion-img>
</ion-avatar>
<h2>{{item.title}}</h2>
<p>{{item.body}}</p>
</ion-item>
</ion-list>
</ion-content>
In this case we are just using the standard *ngFor
directive to loop over all of the items in our array and create list items for them. Since we are not using virtual scroll, this means that DOM elements are going to be created for every single item. If you try to run the application now, you will likely even notice there is a considerable amount of lag just in loading the app.
Create a Virtual Scrolling List
Now we’re going to create a list that uses virtual scroll.
Modify about.html to reflect the following:
<ion-header>
<ion-navbar>
<ion-title> Virtual Scroll </ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list [virtualScroll]="items">
<ion-item *virtualItem="#item">
<ion-avatar item-left>
<ion-img [src]="item.avatarUrl"></ion-img>
</ion-avatar>
<h2>{{item.title}}</h2>
<p>{{item.body}}</p>
</ion-item>
</ion-list>
</ion-content>
The syntax is a little different, this time we are using [virtualScroll]
and *virtualItem
, but the concept is the same. We are setting up some data that will be looped over for the list. The only difference now is that this list will now use virtual scrolling instead of the standard scrolling.
If you were to now remove the code for the standard list, and run the application with just the virtual scrolling list, you shouldn’t notice any lag anymore. You can scroll the list as long and as fast as you like and it will appear to behave just like a normal list, but behind the scenes all those DOM elements are being recycled to improve performance.
When using Virtual Scroll, remember to keep in mind the performance tips list in the documentation, these include:
Using
<ion-img>
instead of<img>
which will prevent unnecessary HTTP requests being made to load images that are not seen. Image sizes should be kept constant.It’s important for the virtual scroll to know approximately how big your items will be, since it needs to know how many items would be required to fill up the screen. You can help this process by specifying an
approxItemWidth
andapproxItemHeight
if it does not already suit the default of40px
.Avoid changing the dataset used to generate the list, as the entire virtual scroll will need to be reset and this is a costly operation.
virtualTrackBy
should be used for a changing dataset.
Summary
For small data sets, virtual scrolling most likely won’t make too much of a difference, but if you are dealing with large amounts of data in lists then it is vitally important that you use virtual scrolling.