Building infinite scrolling in the PayPal mobile web app

By

In this post I will talk about my internship project which consisted of building the infinite scrolling for the user’s activity on the PayPal mobile web app.

Scrolling is often referred to as a painful task. Infinite scrolling, for example, enhances the user’s experience by allowing new content to be fetched as they scroll. However, there are some considerations to make when the content changes inside the scrolling area:

  1. Can the content vary in dimensions? This is required to render the content in order to know how many pixels it will occupy on the screen.

  2. Assuming infinite data, how is the system going to handle the creation of many node elements in the DOM tree?

  1. How often does the app check for the size of the element that wraps all the content?

By looking at some applications we get some sense on how to address these issues:

1. The Web Browser

Chrome divides the web page into tiles called composited layers that then are painted by a given scroll position.

infinitescroll1

2.  Maps

Maps are also divided into tiles, so only certain regions are downloaded and rendered on the screen.

As we can see they both fetch the resources that are needed. They also have the ability to reuse resources by a process called recycling.

In the world of mobile development, recycling is implemented in Cocoa Touch and the Android SDK.

Let’s consider how recycling is handled in Cocoa Touch:

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"cell1"];
    if ( cell==nil ) {
      // Allocate a new cell
    }
   // Set the model for the cell
}

This is a delegate method used to request cells. This setup attempts to reuse a cell that is no longer visible or it will create a new one if none is available. The benefits from doing this are less power and memory required to update the rows.

We wanted to implement a similar approach for our mobile web app by loading new transactions as the user scrolls.

Key observations

  1. Each row had the same height, so we could get the position of any given row in constant time without directly asking the browser.
  2. We had two types of rows, odd and even rows. Both rows had specific styles. For instance, odd rows had a lighter background color compared to even rows.

Implementing the scroll

When we started the project there was not a built-in CSS property for scrolling that all the web browsers we were targeting accepted. We wanted to maintain the momentum style that characterizes native applications. For us, the solution was iScroll, a JavaScript library that solves the cross-browser support issue.

Before starting with the infinite scrolling implementation, we required to give certain structure to the data coming in from the RESTful service. We used Backbone.js in the entire app, so we defined the Model, View and Collection as follows:

The Model

The model consists of essential information about the status of a transaction on PayPal.

{
 "id": "1",
 "name": "Pizza Restaurant",
 "status": "completed",
 "amount": "10.0",
 "currency": "USD",
 "date": "2013-12-01T19:20:30+01:00"
}

Part of this information is rendered in the activity list, and the other is supplementary for the details interaction.

The View

The app has a main view called the CellView which is extended by an OddCellView and EvenCellView views.

var CellView = Backbone.View.extend({
  tagName: "div",
  className: "ui-cell-view"
});

var OddCellView = CellView.extend({
  className: "ui-cell-view ui-cell-view-odd"
});

var EvenCellView = CellView.extend({
  className: "ui-cell-view ui-cell-view-even"
});

The two child classes are meant to represent different objects that share the same functionality but different looking as their className suggests. The benefit from explicitly defining this in the code rather than letting the browser guess it (using :even and :odd pseudo classes) is the Cell Recycling process that will be covered next.

Since all the cells have the same height and are the only one in a row, we can determine the total number of rows we need given a scrolling area height.

TotalRows = (ScrollingAreaHeight / RowHeight) * 3/2

The 3/2 factor means that it will have 1.5 times more rows than the visibles to the user.

The Collection

As PayPal users may have lots of activity status, the app needed to fetch them efficiently. Backbone collections was the structure that helped us to keep the data organized in the client. We built a paging mechanism that fetches models on-demand, so as the user scrolls it will ask for data when there is not enough models to cover the scrolling area. The collection had a limit of 100 contiguous models.

One of the benefits of this separation in the client is that once a model is stored in the client, there’s no need to ask the server for more information about that particular transaction which helps to reduce the stress on the servers.

Putting all pieces together

Now that we have the structure of the data and the views, we can start working on the implementation of the infinite scroll. The core of the infinite scroll is the ability to reuse cells that are no longer visible.

When a cell (DOM element) is rendered in the browser, we create its own composited layer by adding the 3D transform property that gives its position on the scrolling area. This composited layer is then uploaded to the GPU as the cell represents the smallest portion of the scrolling area that can be split into tiles. The implementation also had a composited layer for the scroller (implemented by iScroll) which gives the momentum feeling.

We wanted to avoid operations like: change the background color, change the class selectors, or change the entire subtree of the cell because they invalidate the texture in the GPU causing overhead and in some situations a disgraceful blinking effect.

infinitescroll2

 

The recycling process consists on moving the least recently seen cell to the space that needs a new cell. The following graph illustrates the process:

infinite3

 

The implementation only updates the Y component of the transformation matrix that gives the position of the cell on the screen. This update is really fast since the texture is hardware accelerated.  Next, it updates the model of the cell which indeed requires to paint the new name, status and amount of the transaction, but on a much smaller area.  Another consideration is that web browsers are highly optimized to handle modifications to Text Nodes when those do not involve alterations to the DOM structure.

Results

The scrolling feels responsive. This implementation achieved an average of 60 frame per seconds on an iPhone 5, and here’s what the frame graph looked like on Chrome after fetching about 300 transactions.

infinite4

Here’s a screenshot of the PayPal User’s Activity.

 infinite5

 

 

If you’re interested in an internship at PayPal please visit our Student Careers site.