Tuan-Anh Tran

How to lazy-load CSS with JavaScript

Posted on August 28, 2014  •  2 minutes  • 363 words

Prioritize above the fold content and defer loading CSS can significantly improve your site’s rendering speed (impression, at the very least) and PageSpeed score. Basically, you will inline those CSS rules so that content above the fold (content visible without scrolling down) can be render right away (instead of getting blocked while waiting for the whole CSS file being downloaded), making visitors feel that the site is responsive.

You can use this bookmarklet to identify the critical CSS rules. Note that this will not pickup the @media query. That, you will have to do by yourself for now.

Once you’ve finished identifying those above-the-fold CSS rules and inline them, you will have to lazy-load the rest of the CSS with JavaScript.

If you’re using jQuery, put this in your ready block. Otherwise, write your own version of ready and put it there

This is an example of ready in pure JavaScript. I don’t need jQuery anywhere else on my site so including jQuery is too much overhead for this purpose.

(function(funcName, baseObj) {
  funcName = funcName || "isReady";
  baseObj = baseObj || window;
  var readyList = [];
  var readyFired = false;
  var readyEventHandlersInstalled = false;
  function ready() {
    if (!readyFired) {
      readyFired = true;
      for (var i = 0; i < readyList.length; i++) {
        readyList[i].fn.call(window, readyList[i].ctx);
      }
      readyList = [];
    }
  }

  function readyStateChange() {
    if ( document.readyState === "complete" ) {
      ready();
    }
  }

  baseObj[funcName] = function(callback, context) {
    if (readyFired) {
      setTimeout(function() {callback(context);}, 1);
      return;
    } else {
      readyList.push({fn: callback, ctx: context});
    }
    if (document.readyState === "complete") {
      setTimeout(ready, 1);
    } else if (!readyEventHandlersInstalled) {
      if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", ready, false);
        window.addEventListener("load", ready, false);
      } else {
        document.attachEvent("onreadystatechange", readyStateChange);
        window.attachEvent("onload", ready);
      }
      readyEventHandlersInstalled = true;
    }
  }
})("isReady", window);

Function to lazy-load CSS

function loadCSS(href){
  var ss = window.document.createElement('link'),
      head = window.document.getElementsByTagName('head')[0];

  ss.rel = 'stylesheet';
  ss.href = href;

  // temporarily, set media to something non-matching to ensure it'll
  // fetch without blocking render
  ss.media = 'only x';

  head.appendChild(ss);

  setTimeout( function(){
    // set media back to `all` so that the stylesheet applies once it loads
    ss.media = 'all';
  },0);
}

Once ready, load the rest of the CSS

isReady(function() {
  loadCSS('/assets/css/main.css');
});
Follow me

Here's where I hang out in social media