If you write javascript which dynamically loads CSS files on your page, you have probably seen PageSpeed Insights complain to you about removing render-blocking CSS. Fixing render-blocking javascript is relatively easy: just add an async
attribute to the script, and include it before the closing body
tag, or use an async loader like Google Analytics. Fixing render-blocking CSS is a little trickier. We employ a little media query magic to get the job done.
// append css dynamically to head var head = document.getElementsByTagName('head'); var element = document.createElement('link'); element.rel = 'stylesheet'; element.type = 'text/css'; element.href = '//www.my-external-css.com/my.css'; //here's the magic element.media = 'non-existant-media'; head.appendChild(element, head.firstChild); setTimeout(function () { element.media = 'all'; });
How it Works
When your browser is creating nodes from a stream of HTML, if it runs across a link
tag with rel='stylesheet'
, it will stop rendering until the stylesheet has loaded (or a long time passes, which results in a horribly ugly FOUC). The browser only stops rendering if the media
attribute of the link
tag matches the current media. In HTML, a link
tag’s media
attribute defaults to screen
, which will always match when someone is viewing your website.
We take advantage of this feature by initially setting our tag’s media
attribute to an impossible to match media (e.g. 'non-existent-media'
). That way, when the tag is parsed, the browser does not wait for it to load to continue rendering. When the next tick of javascript executes, we set the media attribute back to a matching media, so that when it loads, we will be able to see it’s effects on the page.
This approach works well for us at AddThis, because the tools we render on user’s websites are secondary in importance to their actual content. While we are loading our CSS, the website is still rendering content that a visitor came there to see. Once the CSS has loaded, we render our tools on the user’s website.