Infinity.js: Smoother Scrolling Through Longer Lists
August 17, 2012
Infinity is available on Github
Fancy CSS3 effects can make your pages look beautiful… But they’re slow to paint. Combining them with long, infinitely-scrolling lists can quickly bring your browser to its knees, because every scroll event causes a repaint — and scroll events can fire dozens of times a second. A few months ago, this hardly affected Airbnb; our pages were self-contained and straightforward, and would never run into that kind of performance issue. When we started to build Wish Lists, though, that changed.
Wish Lists were a new UI paradigm for Airbnb, and we didn’t yet have frontend infrastructure in place to deal with increasingly-long lists of complex HTML entities with expensive styles. On mobile platforms, this is a solved problem: iOS engineers are familiar with Apple’s UITableView, as are Android engineers with Google’s ListView counterpart. There were some solutions we found that would work acceptably for mobile web browsing, but we couldn’t find good desktop-web libraries that would meet our needs for silky-smooth browsing. So we built our own.
Infinity.js is a fairly simple tool that does complex things quickly. As you scroll through a page of content, Infinity will check the position and size of your viewport, and move elements in and out of the DOM as you scroll. It runs on a throttled scroll event — you can configure the timing yourself, but by default it only pays attention to events every 350 milliseconds — and has a small buffer of content above and below your current scroll position to handle any missed scroll events.
As you add elements to Infinity’s ListView, it breaks the content given to it into somewhat-screen-sized pages, and caches the positioning and sizing data of those pages to minimize expensive DOM hits. It only makes changes to the DOM once you’ve scrolled through at least a screen’s worth of content, so most of the time it’s doing almost nothing at all. The exact size of the buffer pages can be configured as well, and selecting an appropriate size can help improve the performance of your pages considerably. By default, it’s set at 3 times the height of a screen: this minimizes the number of swaps Infinity has to perform. The larger the value is, the fewer swaps it will do — but the larger the buffer size, the longer your repaint times will get, so there’s some tradeoff to increasing that value.
Infinity doesn’t only reduce repaint times, though — it also reduces the amount of time it takes to do a reflow. Since content is actually removed from the DOM, and not just hidden or set to display:none, there are fewer DOM elements to traverse during reflows. Once enough content has been added to your document, this difference can become quite noticeable.
We’ve tried to make Infinity as simple as possible, and for the set of actions it supports it mimics the jQuery API where appropriate.
Read the full API documentation
Fork the repo on Github