Blog

Inside the Minds of the Machine

Talking Tech

Optimizing JavaScript for Performance

Given the importance placed on site speed and performance (not only by us, but also by search engines), it’s crucial to make optimization a regular part of the development process. Having a product that works is great, but having a product that is lightning-fast and as efficient as possible is even better! I recently worked on optimizing the JavaScript for our THIRTEEN Explore app for Fire TV, and will offer some easy tips and tricks to implement to improve page speed.

Avoid performance lag by using CSS to deal with host objects/DOM elements whenever possible.

If you have any kind of animation or interactivity on your page, try to write this in CSS whenever possible, instead of JavaScript. CSS transformations and interactions, such as :hover, :after, and @keyframes are actually optimized by browsers to perform very well. Of course, on a lower-level, this code is still executed by JavaScript, but you’re letting your CSS do more of the heavy-lifting, which it has been optimized to do over the years.

Therefore, instead of

 

It’s much more efficient to put this directly in your CSS:

If the event that triggers your interaction or transformation doesn’t correspond to a CSS selector, such as a click event, it’s best to create a CSS class to handle the transformation, instead of implementing the change via jQuery.

For example, instead of this:

This is much more efficient:
JS:

CSS:

Ensure faster DOM traversal by batching CSS changes, using JavaScript selectors, and having a lean HTML file.
With jQuery, it’s a breeze to select elements on the page, even nested ones, with the $() selector. However, this requires jQuery to traverse the DOM and each subset of DOM elements until it finds its match, which can be time consuming. Furthermore, the $() selector returns a jQuery object, not a raw DOM element, which is actually more useful when manipulating elements. Therefore, it’s much more efficient to use the JavaScript method document.getElementById(), and return an actual DOM element whenever possible.

Another way to reduce the amount of DOM traversal in your page is to batch CSS/DOM changes whenever possible. Every time you write a DOM change with jQuery, such as:

The browser is prompted to re-render the entire page with the new style. It’s much more efficient to batch multiple changes to the same selector so the browser only needs to re-render once. This means that instead of writing this:


Which would prompt the browser to re-render itself three times, you could combine these into a single batch:

Lastly, try to preen and organize your HTML file to include tags that are only absolutely necessary, and avoid unnecessary nesting. The more tag-heavy and complex your HTML is,the more time it will take to for jQuery to traverse and modify the DOM, since every element needs to be scanned checked against the selector. This means that reducing the number of HTML tags by 50% will double the speed of your DOM traversal. Considering the fact that DOM traversal creates a considerable performance drag in a JavaScript app, this reduction can make a noticeable difference!

Use low level/native constructs, and beware of methods that are slower.
Array methods such as .push(),.pop(), and .shift() are very efficient because they closely resemble their low-level assembly language counterparts. In addition, array manipulation is much quicker and more efficient than object manipulation in JavaScript– if you find yourself needing a lightweight data structure in your page, and can use an array, you should do so! Arrays also let you mimic queue and stack structures for data handling, which can really simplify your logic.

There are some methods to watch out for, however. For example, Array.push(data) is faster than Array[index]]=data, which is faster than Array.unshift(data), which is actually quite slow. Setting Array[index]=null is slower than just deleting the element. These differences are fairly small, but it’s good practice to be aware of certain methods that could slow you down a little bit, and what better options might be.

Use switch statements

If you only have a couple conditions to test, then an if/else if/else statement is fine. However, in our app, there were areas where we needed to check as many as 7 or 8 conditions. In this case, it’s more efficient to use a switch statement. Switch statements can be optimized more easily during compilation (they may be compiled into a branch table or binary search) which leads to faster, more efficient code.

If you do find yourself using if/else statements with many conditions, it’s a good idea to put the most often true statement as early in the code block as possible, to avoid unnecessarily running through non-applicable or less commonly true conditions.

Remember to remove all of your console.log statements!
This should go without saying, but if you rely on console.log for troubleshooting and debugging purposes during development, make sure you remove all of them before launching your app. Not only could anyone with just a tiny bit of programming knowledge read your debugging statements, but these little console.logs peppered throughout your app can lead to serious performance lag. Below, I used jsPerf to test the performance of calling an empty function against a simple console.log statement. This test revealed that with the browser console closed, a call to console.log ran 73,959 less times per second than an empty function does, which is a fairly significant gap.

With the browser console opened, a call to console.log ran 769,326 less times per second than the empty function– that’s huge!

If you want to avoid combing through your code and taking out all console log statements by hand, you can use Google’s Closure Compiler or JS-build-tools, which support ‘conditional compiling’, which will remove your console log statements upon compiling your code for production.

On a similar note, although it should also go without saying, be sure to remove all ‘debugger’ statements from your code. Even if a debugger sits within a condition that is never met, it is unsupported syntax in a compiler, and the containing function will not be optimized upon compilation (the entire function is ‘punished’).

Use native functions
ECMAScript provides a lot of built-in functions that will save you time, both in writing programs and in page performance. In our FireTV app, I always used === instead of == for equality comparison, for a small (albeit frequently used) performance boost. In addition, native constructs such as Math.floor(), Math.round(), newDate().getTime(), etc. allow your code to compile faster, and are much better for performance.

Whether you are keeping efficiency in mind throughout your development process, or revisit your code with aims to refactor improve it, being aware of all the powerful built-in tools that JavaScript and CSS have to offer can make a big difference.