Front End Performance Strategy: Scripts

  • 7 minute read

In the last installment of this series, we considered CSS optimization. This time we’re going to look at the impact of scripts.

Remember, as architects and developers, it’s up to us to inform stakeholders about the impacts of their choices, offer compromises where we can, and implement in smart and responsible ways.

So, picking up on our last post, most everything about the way Drupal handles CSS holds true for JavaScript, with a few notable exceptions.

CSS aggregation removes whitespace, but JavaScript aggregation done using Drupal core's aggregation system doesn't do that or any other form of minification or uglification. It simply concatenates our scripts.

Like CSS, JavaScript also has three groups:

  • Library - Libraries, via drupal_add_library
  • Default - Modules
  • Themes - Your theme

Drupal creates aggregates for each of these three groups in the head, but can also deploy to the footer when the scope is set to ‘footer.’

When and where to load your JS

Drupal_add_js features a great option in the options array called scope that allows us to load JavaScript in the footer. This helps decrease our visual page load times by moving render-blocking JavaScript out of the way to a place where it won't impede the loading of other assets (like images, styles, other scripts).

The options array also provides an option called type which defaults to ‘file.’ When using the default option of ‘file,’ it tells Drupal that this is a script hosted on our site, so it's eligible to be aggregated. Combined with the every_page flag set to ‘true,’ just like with our CSS, these scripts get aggregated with the scripts added using ‘.info’ files into the big site-wide aggregates. If the every_page option is left out, or it is set to ‘false,’ then these scripts are aggregated as one-offs outside of our main three site-wide JavaScript aggregate files, again, just like with our CSS.

The type option can also be used to create inline scripts, which can be handy in a couple of ways. It will print our JavaScript directly into our header or footer depending on scope, but it's also useful for dynamically loading external scripts so they become asynchronous. Going forward, the async_js module is probably the way to go. I personally haven't had the opportunity to try it out, but I look forward to the chance. If you've used it, let us know in the comments how it worked out for you.

The third 'type' is the one we use for loading external scripts, which we seem to do often these days. Using an asynchronous method, mentioned above, is important because of the additional round-trip time to get the script. However, a slightly less effective way to handle it without using the older method of an inline script, or an additional module, is simply scoping the script to the footer and setting the type option to external (which prevents it from being aggregated).

Unlike with CSS, I'm less inclined to add JavaScript on every page because I like to scope JavaScript to the footer whenever possible. Since it isn't blocking render down there, the additional HTTP request doesn't really bother me. Generally, if it's on most pages, or a page visited by most users, go ahead and add it to every page. If it isn't and it's scoped to the footer, then only add it when it is needed. If it has to be in the header, and on an obscure page that isn't frequently visited by users, you're probably going to need to do some A/B testing to compare the performance hit on the obscure page by not including it on all pages vs. the performance hit on all the other pages by including it on all pages. I like to err on the side of the majority, meaning, I tend to only include the JavaScript on the obscure pages.

JavaScript: Know when to say when

You can do almost anything with JavaScript, and leveraging a framework like jQuery makes it easier to want to do everything with JavaScript. However, in addition to blocking page render and increasing the size of the page that has to be processed by the browser, there are other performance considerations with JavaScript.

It runs locally, in the browser, which means it uses a visitor's memory and processor. Poorly written or heavy use of JavaScript can lead to a poor user experience in the form of everything from delayed and choppy animations to browsers becoming unresponsive and/or crashing. For simple animations, consider using CSS3 animations, benchmark them using a tool like Chrome's dev tools or Firebug, and go with the least expensive performance option (these usually end up being the smoothest animations as well).

These script performance problems are often magnified on mobile devices where the hardware resources are more scarce and we often resort to using more JavaScript to solve challenges presented by the smaller viewport. This should reinforce the importance of a mobile first strategy, not only for design but also for development. It also highlights the need for open communication between the product owners, the design team, and the development team.

Conclusion

Scripts, like styles, contribute front-end implementations that can seriously hamper Drupal’s back-end magic. By favoring stylesheet aggregation and reigning in exuberant preprocessing, we can save the browser a lot of work. Applying the same principles to JavaScript, while properly placing scripts in the header or footer-based on function, can also improve our page-load times.

Next time, in our final post of the series, we’ll take a grab-bag look at some subtle, more specialized techniques that just might shave off those last few milliseconds. Stay tuned for a post covering Content Delivery Networks (CDN), semantic HTML, and how to encourage improved client-side content selection.