Skip to content

Performance Blog: How to Get the Most Out of JavaScript

DBS Interactive

 

Graphic stating How To Get The Most Out of JavaScript

Now that JavaScript has won the battle for the most important programming language for next generation web, mobile and desktop applications, web developers can get serious about how to use JavaScript efficiently and effectively. We need JavaScript to build applications, and we need it to enhance the user experience — not detract from it. User experience is more than look and feel, it’s usability, responsiveness and performance too. A recent survey showed that 54% of abandoned shopping carts were due to “slowness.”  Nobody likes  “slow.”

This series of articles will look at things web developers can do to get the most performance out of every line of code, and will focus on JavaScript. HTML5 may never be as performant as “native” code, but we can certainly do our best to get as close as possible in order to satisfy our users, who have become spoiled with the responsiveness of the native app experience. Again … nobody likes “slow.”

Treating HTML5 and next generation web applications as just the second coming of the web doesn’t work well from a performance and total user experience perspective. Mobile devices just aren’t desktops. They don’t have the same hardware capabilities — they have much less memory and processing power. Their connectivity to cell networks is erratic and slower than typical desktops, and cell network latency is radically higher. And there are battery life issues. So why throw desktop code at them? It’s not a good idea.

Of the many things we could do on the client side, sadly there aren’t any single magic bullets to make slow web apps fast. There are, however, many individual things that taken together will help.

Before we get into specifics, let’s look at some dynamics. JavaScript is powerful and a bit of a wildcard. It can do *anything* within the document context — create DOM, destroy DOM, move DOM, modify DOM, redirect to another page and so on. The browser cannot really know exactly what is going to happen until the last line of JavaScript is parsed. So the browser stops rendering any time it hits JavaScript. This effect is known as “blocking.” The page rendering is blocked while JavaScript is being executed, and, if external, being downloaded. The user will experience a freeze like situation. Ever have to wait to interact with a page that has rendered content? You are experiencing “blocking”. Other things can cause blocking, but JavaScript is a big culprit. Our goal is to eliminate blocking where we can, or else mitigate it.

First on the list is how much JavaScript do we really need on this page load? It’s tempting to include multiple JavaScript libraries and helpers on every page. That’s some good stuff, right? Maybe we’ll need some of that good stuff. Even ones that might not be needed on every page load. That nice jQuery carousel plugin? Does it run on every page? Do we need it on every page? jQuery validate? How many pages will really need that?

But after all you might say, the browser caches external JavaScript files, so there is little performance hit after the initial page load, right? That’s desktop thinking. Mobile devices will also cache, but their caches are much more limited, and then on each new page load, that cached JavaScript still needs to be parsed and evaluated as to what of the current DOM does it need to operate on. If that turns out to be nothing, then why are we including that JavaScript in the first place? We are adding overhead, that will be more noticeable in slower browsers and mobile devices. Use conditional logic in your templates to include only JavaScript that will actually be used. Know your code! Use your server side language to server just what is needed for each page load.

Secondly, now that we know what JavaScript we need, it’s also important where we put it. To mitigate the impact of “blocking“, we want any JavaScript that is not needed by the initial page rendering to be loaded as late as possible in the rendering process. This will give the browser a chance to render most of the page, and will give the user something to look at while the rest of the JavaScript is happening. There will still be blocking, but it’s blocking after the initial rendering. So put scripts at the end of the end of the document whenever possible.

Screenshot of Javascript

Other alternatives to the placement of JavaScript, are the defer and async attributes. defer is well supported in all browsers (even IE, and was actually an IE innovation). The browser will not stop rendering immediately when a script tag with defer is encountered. The script will be downloaded and processed later (but blocking will still occur during the actual, “deferred” JavaScript execution phase). async is an HTML5 attribute, that forces the downloading  to happen asynchronously (in the background). Blocking does not happen during this phase, but again blocking will happen during the actual execution of the JavaScript. So these can be helpful but are not panaceas. There is nothing wrong with including both if you are concerned about cross browser compatibility.

Another helpful technique is dynamic script tag injection:

$( function() {
$('body').append('<script src="//example.com/js"></script>');
});

This will cause the script element to be dynamically generated on the ‘ready’ event (DOMContentLoaded) and will also minimize blocking.

Other useful tips:

  • CSS should *always* be loaded for JavaScript!
  • Make sure you are always using the most current version of your preferred JavaScript libraries. jQuery 1.9.* is significantly faster than older versions, for example.
  • Be sure to use “minified” versions of any external JavaScript files (and CSS for that matter as well). This does not speed up execution, but will help download times.
  • Inline small chunks of JavaScript code as opposed to putting that code in an external resource (this does not speed up the execution but does save one request to the server).
  • Where possible, combine multiple JavaScript files into one file. Again no speed up of execution, but this saves requests and overall application overhead and performance.
  • The DOM ‘ready’ event fires after the document is parsed and the initial DOM is built. The DOM is there, but images are probably still being downloaded and placed, CSS is probably still being applied, and probably not all scripts have been executed yet.  So the page is not fully rendered at this point. JavaScript run here can still  cause unwanted blocking. There may be occasions that the window onload event makes more sense for some UX situations, since it fires after the initial page is fully rendered, aka “loaded.”

Resources:

A simple script to help with basic minification of multiple files, here is a script that minifies all CSS and JavaScript recursively.

Google’s Closure compiler that minifies and improves JavaScript performance: https://developers.google.com/closure/compiler/