It is a competitive advantage for websites to be fast and responsive. When building the AddThis Smart Layers tool suite, performance was a priority and first-class feature. Let’s take a look at some of the mobile and desktop performance best practices that I (and the rest of the AddThis team) used to make Smart Layers blazing fast.
Dynamic asset loading is becoming popular in the front-end community, and is often a benefit of using a client-side script loader, such as Require.js (which we don’t use).
The unofficial performance goal for web animations is to perform at around 60 frames per second (fps). If your app is running animations at 60fps, then your users will see smooth and jank-free animations. Smooth animations = happy users.
To achieve ~60fps performance, Smart Layer animations use CSS transitions and transforms. A major performance benefit of using CSS transitions and transforms is hardware acceleration. At a high level, this means that all of the animation hard work can be offloaded to the browser and GPU. Performance benchmarks have shown massive speed and resource consumption improvements for browsers that utilize these techniques (specifically mobile browsers).
Instead of creating our own custom CSS transforms and transitions, we decided to use a few of the CSS-based animations from the popular project, Animate.css. We love open-source technologies and want to thank Dan Eden for all of his hard work on the project.
Proper memory management is crucial for the performance of web applications. A common reason for a web app feeling sluggish and non-responsive to a user is a memory leak.
You’re right, your browser does automatically clean up and deallocate memory. Unfortunately, the browser does not know your application, and you must make it clear what code is no longer being used and can be freed up for other things.
Since so many web applications these days are single page applications (I know, I’m tired of that buzz word too), and long-lived (meaning they go for a long time without a page refresh), we wanted to make sure to support memory deallocation for Smart Layers. To support this, Smart Layers provides a
destroy() method that allows you to remove all DOM nodes and DOM event listeners it creates. This method will trigger the browser to garbage collect and deallocate the memory used by Smart Layers to other parts of your application.
This brings us to another important topic: memory consumption. Smart Layers uses a variety of techniques to make sure that it is taking up as little memory as possible, leaving more for your application. Here are a couple techniques.
Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.
But how does the unordered list click handler get triggered when a child list item is clicked? That’s where event bubbling comes in.
Smart Layers heavily uses event delegation and event bubbling to minimize the number of event handlers that are bound. While testing Smart Layers on mobile devices, there was a noticeable performance improvement when using event delegation instead of direct element binding, since less memory was being used.
Let’s reiterate; DOM interactions are one of the most memory-expensive actions a web application can complete.
A common performance pitfall is repeatedly appending DOM elements (causing multiple browser reflows) to the page instead of appending once. With Smart Layers, we only append elements to the DOM when absolutely necessary. For each Layer, we construct all of the HTML that we want as a string, and then append that string to the DOM. This prevents unnecessary browser reflows, which force the browser to re-calculate the layout of the page.
Another best practice that Smart Layers uses to improve performance is simplified DOM selectors and DOM caching. If we can select a DOM element using an id, then we do. We also save those elements as instance properties internally, so that we do not need to re-query the DOM again.
For any complicated selectors (not too complicated, mind you), we use qwery, a compact, blazing fast CSS selector engine. We are not using Sizzle (the selector engine used by jQuery) since we are not using jQuery, and we do not need all of the advanced selectors Sizzle provides. A special shout out goes to Dustin Diaz, Jacob Thornton, and Rod Vagg for their amazing work on qwery.
Debouncing High-Rate Events
Smart Layers listens to both the resize and scroll events for different reasons, and we make sure to debounce each event to prevent from blocking the UI thread.
One of the cooler features of Smart Layers is our responsive toolset. If you are on a desktop browser and the viewport is less than the size of a tablet browser, we automatically provide our mobile tools (which look better in a small viewport) using the resize event.
We use CSS3 media queries, in conjunction with the debounced resize event, so that we can allow users to specify which viewport sizes they would like our mobile tools to show up on for desktop browsers. Configurable + good performance = happy users.
On mobile browsers, we allow you to scroll through all the possible sharing services that we support. There are hundreds of services, and we are using beautiful, retina-ready, SVG icons for each service. To prevent any performance issues, we do not load all of our services at once.
If a user searches for a service, or scrolls to a position on the page where that service should be, then we load the service (and it’s associated icon). This technique allows our users to enjoy beautiful icons, and not get bogged down by any performance issues.
Responsive Mobile Scrolling
When testing Smart Layers on mobile browsers, we noticed that scrolling through a large unordered list with a flick of the finger did not send the webpage scrolling. Since we did not feel this was a great user experience, we started to investigate how we could incorporate this momentum effect.
We found out that you can add this default behavior on iOS 5 using the
-webkit-overflow-scrolling: touch; CSS property. This is great, but what about other platforms/browsers? To support other browsers, we incorporated the popular library, iScroll. A big thank you to Matteo Spinelli for all of his hard work on iScroll.
We hope that all of these performance best practices baked into Smart Layers pay dividends for you. If you haven’t already tried Smart Layers, grab and code and let us know what you think!