Optimizing CSS & JavaScript

CSS and JavaScript in External Files

Previously, we talked about how important it is to reduce HTTP requests if you want to have fast-loading web pages.

Wouldn't it be logical, then, to put all of your CSS and JavaScript inline into the source code of the HTML page that needs it? The obvious benefit would be that we'd save some HTTP requests, since CSS and JavaScript wouldn't have to be downloaded separately anymore.

However, there's a big catch: we would have to transfer these contents to the user on every subsequent page load, again. With such a strategy, you would completely miss out on the merits of caching. Therefore, instead of including CSS and JavaScript in the page's HTML, we are better off delivering them as separate resources. Only then can they be cached effectively.
Have a look at our chapter on HTTP caching for details on how to establish an effective caching strategy.

Also in the spirit of keeping HTTP requests at a minimum, keep in mind that you should deliver as few files as possible. When it comes to CSS and Javascript, there are lots of preprocessing tools that allow you to combine multiple source files into a single (or at least fewer) output files.
Have a look at our chapter on Combining CSS and JavaScript earlier.

Minifying CSS and JavaScript

Human-readable text and optimized text are really two kinds of things. For example, for us as developers it makes a lot of sense to have comments in our JavaScript files. The browser's JavaScript interpreter, however, doesn't care about these. They are simply a superfluous burden that makes files larger than they have to be.

/* This is the original CSS code.
    Including comments, lots of white space, ... */
p {
  text-align: center;
}

div {
  background: red;
}

While comments are a very obvious example, optimizing CSS and JavaScript for the browser goes a lot farther than this: we could go without white space and new line characters; we could shorten variable names; we could combine duplicate CSS selector rules...

p{text-align:center}div{background:red}

Taken together, all of these (and a couple more) measures are referred to as "minification". And they aim for one goal: less characters and therefore smaller file sizes, a cornerstone metric for fast-loading web pages.

Luckily for us, we don't have to re-educate ourselves to write our JavaScript and CSS without white space or comments. Enough great tools are here to help us when it comes to "minifying" our resources.

Frameworks

If you're using an application framework like Ruby on Rails or Django, you might already have a minification tool on board. Check your framework's documentation to see if it has something like this built in.

Grunt & Gulp

If you're using Grunt or Gulp as part of your workflows, there are lots of plugins out there to "uglify" and minify.
JavaScript: grunt-contrib-uglify and gulp-uglify
CSS: grunt-contrib-cssmin and gulp-clean-css

Desktop Apps

If you prefer a standalone Desktop application for this job, you should take a look at tools like CodeKit and Prepros.

Including CSS in Your Pages

You might be tempted to think that including CSS is a real no-brainer. And you're almost right. But the "critical rendering path" (the way browsers go about rendering a page) offers some opportunities for optimization.

By default, the browser has to wait until all CSS resources are downloaded before it can start rendering a page. Broadly speaking, this means the more CSS you have, the later will your page start to get painted.

However, there are a couple of things we can do to optimize this.

Media Types and Queries

When including your styles, the media attribute can be very helpful. For example, by marking it as "print" the browser will not have to wait until it is downloaded and parsed.

<link href="print-styles.css" rel="stylesheet" media="print">

Another way to make CSS a so-called "non-blocking resource" is to use a media query. The browser can then decide on its own if it has to download the CSS (and thereby delay rendering of the page). In this example, the rendering will only be blocked on smaller screens where this particular CSS is really needed:

<link href="mobile.css" rel="stylesheet" media="(max-width: 480px)">

Deliver and Include CSS as Early as Possible

Since the browser (by default) has to wait for all CSS to be loaded, it's important to provide it as quickly as possible. We've already discussed a couple of things you can do: minify your CSS files and compress them using GZIP.

However, there's also another very simple way to make sure the browser receives CSS as early as possible - by including it early in the HEAD section of your HTML document. This way, the browser will start downloading CSS as soon as possible.

Don't Use @import

CSS allows to "import" additional stylesheet files right from within a CSS file:

@import url('style.css');

However, you should by all means try to avoid this mechanism. The reason for this advice is simple: the CSS @import delays rendering of the page!

To give you an example, let's say you include a file named "styles-1.css" in your page. If somewhere in that file, the @import statement is used to load additional CSS from "styles-2.css", the process is slowed down immensely:

  1. First, as usual, styles-1.css has to be downloaded and parsed.
  2. Only after parsing is finished will the browser notice that another file, "styles-2.css" is referenced.
  3. It then has to download and parse this file, too, before it can finally start rendering the page.

Of course, the @import statement was once invented for a good reason: it allows to keep your CSS modular, in separate files. This underlying principle is perfectly reasonable and you shouldn't abandon it!

However, production CSS is not the right place for it. Take a look at the Sass and Less preprocessor frameworks where an @import mechanism is also available - but without the negative side-effects.

Loading JavaScript

Similar to CSS, JavaScripts are "blocking" resources by default: the browser will stop constructing the page in order to download and parse the JavaScript that it just encountered. Only once it's done will it continue to render the page. Especially with bigger amounts of JavaScript (which are quite common on today's dynamic and script-heavy pages), this will let your visitors wait until they finally see your page.

Loading Scripts at the Bottom

One way to improve this situation is to include JavaScript resources at the very bottom of your HTML document - right before the closing body tag. This allows the browser to calmly construct large parts of both the Document Object Model and the CSS Object Model.
Your page should be displayed or at least begin to display before JavaScript "interrupts" the remainders of the rendering process.

Including Scripts with ┬┤async┬┤

An even better way is to explicitly inform the browser that certain JavaScripts should not block the rendering process. You can do this by adding the "async" attribute to the script tag:

<script async src="page.js"></script>

This solution should cover most needs. If you want to explore this topic even further, there is lots of reading material on the web like this article by Patrick Sexton.

Loading JavaScript from Hosted Services

The majority of a website's JavaScript will be custom coded for that site. But most websites also include common libraries like jQuery or AngularJS. Although you can serve these from your own web server, there's another option: a couple of public services exist that host popular libraries on their own Content Delivery Networks and make them available for developers to include them for free.

Including libraries from one of these CDNs has a couple of advantages:

  1. Reliability and speed: its hard to beat these services when it comes to reliability and speed.
  2. Parallel HTTP connections: browsers limit the number of simultaneous connections to the same server; if you're requesting from a second source (one of these CDNs in this case), the browser can start more parallel downloads. (Read more about this in our chapter on Content Delivery Networks.)
  3. Browser caching: most importantly, chances are that the browser can deliver this from its cache! Given the popularity of jQuery, for example, it's not unlikely that the visitor has already downloaded this resource on a different website.

Loading Scripts Only Once

Although this piece of advice is rather obvious, it's remarkable how often it's still not heeded: especially in larger teams, it's not uncommon that different developers include the same third-party library multiple times. It goes without saying that this an especially tragic waste of bandwidth.

Many application frameworks offer a central function to handle library imports. Part of this is also that it protects you against duplicate inclusions.

Get the Website Optimization Toolkit

About Us

As the makers of Tower, the best Git client for Mac and Windows, we help over 80,000 users in companies like Apple, Google, Amazon, Twitter, and Ebay get the most out of Git.

Just like with Tower, our mission with this platform is to help people become better professionals.

That's why we provide our guides, videos, and cheat sheets (about version control with Git and lots of other topics) for free.