Wednesday, December 2nd, 2009

Google Analytics unblocks the Web w/ Async support

Category: Performance

>I heard a huge cheer from the Internet today and found that Google Analytics has launched async mode which finally unclogs our browsers from blocking.

Steve Souders must have had the loudest cheer, and wrote up his view:

The pain of loading JavaScript files is that they block the page from rendering and block other resources from downloading. There are workarounds to these problems. Chapter 4 of Even Faster Web Sites describes six techniques for Loading Scripts Without Blocking. One of those, the Script DOM Element approach, is the technique used in the new Google Analytics async pattern. Google Analytics’ ga.js file is a perfect example of a script that should be loaded asynchronously – it doesn’t add any content to the page, so we want to load it without blocking the images and stylesheets that give users what they really came to see.

Improved Uptime

What happens if a script takes a long time to load, or fails to load? Because scripts block rendering, users are left staring at an empty page. Google Analytics has an amazing infrastructure behind it, but any resource, especially from third parties, should be added cautiously. It’s great that the GA team is evangelizing a pattern that allows the web site to render while ga.js is being downloaded.

To get going, simply change your ga script code to follow the pattern:

javascript
< view plain text >
  1. var _gaq = _gaq || [];
  2. _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  3. _gaq.push(['_trackPageview']);
  4.  
  5. (function() {
  6. var ga = document.createElement('script');
  7. ga.src = ('https:' == document.location.protocol ?
  8.     'https://ssl' : 'http://www') +
  9.     '.google-analytics.com/ga.js';
  10. ga.setAttribute('async', 'true');
  11. document.documentElement.firstChild.appendChild(ga);
  12. })();

It’s extremely cool to see this pattern being evangelized for such a major piece of the Internet. A few items of note:

  • Obviously, you have to replace “UA-XXXXX-X” with your ID.
  • Since ga.js is being loaded asynchronously, there has to be a way for web site owners to couple their desired GA functions with the code when it finishes loading. This is done by pushing commands onto the Google Analytics queue object, _gaq.
  • Once all your callback commands are queued up, the ga.js script gets loaded. This is wrapped inside an anonymous function to avoid any namespace conflicts.
  • Inside the anonymous function is where we see the Script DOM Element approach being used – with two nice improvements. A ’script’ element is created and its SRC is set to the appropriate ga.js URL. Looking ahead to support of asynchronous scripts in HTML5, the ‘async’ attribute is set to ‘true’. Very nice! The main benefit of this is it tells the browser that subsequent scripts can be executed immediately – they don’t have to wait for ga.js. The last line adds the script element to the DOM. This is what triggers the actual download of ga.js. In most of my code I do document.getElementsByTagName(”head”)[0].appendChild, but that fails if the document doesn’t have a head element. This is a more robust implementation.

Related Content:

Posted by Dion Almaer at 6:21 am
24 Comments

++++-
4 rating from 39 votes

24 Comments »

Comments feed TrackBack URI

Wow, that’s great!

Comment by stoimen — December 2, 2009

omg, first ever use of createElement on
Thank God there is Steve.

Comment by paziek — December 2, 2009

script tag*
got deleted

Comment by paziek — December 2, 2009

Better late than never…

Maybe the GA team can help their Double-”morons”-Click colleagues to fix their tracking scripts too.

Comment by JohnP — December 2, 2009

It’s obviously a HUGE improvement over the default document.write() mess from before. And if GA is the only script you are loading onto your page, I think this is probably the best answer.
.
But, if you are loading a couple (or several) scripts, and you want to take advantage of simple async loading of scripts, you can use LABjs. http://labjs.com
.
The simple code to load GA with LABjs would be this:
.
$LAB
.script((’https:’ == document.location.protocol ? ‘https://ssl’ : ‘http://www’) + ‘.google-analytics.com/ga.js’)
.wait(function(){
pageTracker = _gat._getTracker(”UA-XXX-XX”);
pageTracker._trackPageview();
});

Comment by getify — December 2, 2009

self.promote(+1);

Comment by sixtyseconds — December 2, 2009

I am very glad to see this finally get pushed out to the masses!

Has anyone here seen this on their Tracking Code page’s in GA? I’ve checked my accounts and I haven’t seen it listed there. If I hadn’t caught the GA blog post or this article I’d have never noticed they added support.

BTW, was this code updated since it was posted last night? I swear that when I looked at it before they also added the type attribute to the ga variable.

Lastly, I just checked and if anyone hadn’t noticed Google has re-enabled gzip compression on ga.js!

Comment by blepore — December 2, 2009

This seems very nice, but can someone explain how this is an improvement over putting the GA script right before the closing BODY? Won’t everything before the GA script be rendered before the script is fetched anyway?

Thanks in advance

Comment by mcsnolte — December 2, 2009

This is a great improvement. At Keynote we see many of our client’s sites get delayed due to the placement of JavaScript from an analytics tag. These delays can either be transparent to the user (loads at the end) or actually block the entire page from loading if it is one of the first things to be requested.

Comment by AbelGonzalez — December 2, 2009

@mcsnolte : when directly inserting before the “html” closed tag, it prevents browsers (or JS frameworks) to fire the “pageReady” event. For sites which are dependent on this event to perform some actions (DOM manipulations, …), having a blocker call to google-analytics.com could be a huge problem !

Comment by Remi — December 2, 2009

mcsnolte,

A script right at the end of body will still block running scripts, and possibly other resources that load async.

What I want to know is how this works in IE. Why not add the defer attribute for compatibility?

Comment by eyelidlessness — December 2, 2009

Seems like they should have made the entire way you call the api a lot better if you were making a new version.

Comment by meandmycode — December 2, 2009

…that fails if the document doesn’t have a head element. This is a more robust implementation.

Actually, there’s a problem with document.documentElement.firstChild.appendChild(ga)… in my XHTML documents (at least) document.documentElement.firstChild is a text node, not an element, so the appendChild operation fails. Don’t HTML parsers automatically insert a HEAD element into the DOM even when one isn’t specified in the markup? Wouldn’t document.getElementsByTagName("head")[0].appendChild(ga) still be a better approach?

Comment by westonruter — December 2, 2009

@westonruter: yeah that would be better, document.documentElement.firstChild isn’t a safe way to find a node you can append a script to.

Comment by Jadet — December 2, 2009

http://yura.thinkweb2.com/jstests/document_head_test_by_Garrett.html
.
Try that in certain browsers (like Chrome and Opera 10), and you’ll see that in fact the comment node is the first node returned (which is of course invalid to nest a <script> element under).
.
My feeling is the GA code is *NOT* safe as is. They should use a more robust option.

Comment by getify — December 2, 2009

I’m providing a widget to websites.
When my hosting provider went down, websites complained that the page took to much time to load.

I was trying to simulate this scenario so I’ll be able to fix it by putting a non-existing script url but it didn’t slow down the page.

Can someone recommend a way to simulate a non responding third party script scenario?

Thanks

Comment by pablop — December 2, 2009

pablop: The easiest way is probably to send a .js file with several megabytes of whitespace down the tubes.

Comment by eyelidlessness — December 2, 2009

Wow, Cool!

Comment by tung148 — December 2, 2009

Correct me if i am wrong, but by not waiting for the script, isn’t there a chance that the user can navigate away before the script gets loaded and this the stats get screwed?

Comment by Afraithe — December 3, 2009

Or they could just go with [script defer src=""][/script] to get async scripts working for the majority of users. Won’t work in Firefox though.
<

Comment by Jordan1 — December 3, 2009

Thank you!
i will test it in this website http://www.electric-power.us

Comment by holy2050 — December 3, 2009

Hmm, or just put the *bleep* thing high up in the head to get it over with and have a white page for a few miliseconds.
.
labjs is a nice solution, though people get the opportunity to click away before ga.js fires, that might be an issue for the stat freaks.

Comment by BenGerrissen — December 4, 2009

Afaik Firefox 3.5 supports “defer”, or am I wrong? Plus according to the spec async and defer are not exactly the same – though I do understand they’re pretty close to each other.

Also if one does lots and lots of event tracking (some immediately) then I think it’s better to *wait* for the script to arrive, but that’s a fair trade.

Comment by rosamez — December 5, 2009

i like it
thanks

Comment by Aphrodisiac — January 15, 2010

Leave a comment

You must be logged in to post a comment.