Sam Saffron

The last script holding in your header is usually jQuery, learn how to move it to your footer and why you should.

##Reminder, script tags block rendering

It is common advice to move all your external JavaScript includes to the footer.

Recently, the movement for fancy JavaScript loaders like ControlJS, script.js and so on has also picked up steam.

The reason for this advice is sound and pretty simple. Nothing, Nada, Zilch below non asynchronous or deferred script tags gets rendered until the script is downloaded, parsed (perhaps compiled) and executed. With one tiny exception that is Delayed Script Execution in Opera. There are quite a few technical reasons why scripts block. You need a “stable” DOM while executing a script. Browsers need to make sure document.write works.

Often people avoid the async attribute on script tags cause they usually execute in the order they return this means you need to be more fancy about figuring out when the script is ready. The various JavaScript loaders out there give you a clean API.

##Why is that jQuery synchronous script include stuck in the html ‘HEAD’ section?

jQuery solves the age old problem of figuring out when your document is ready. The problem is super nasty to solve in a cross browser way. It involves a ton of hacks that took years to formulate and polish. It includes weird and wonderful hacks like calling doScroll for legacy IE. To us consumers this all feels so easy. We can capture functions at any spot in our page, and run them later when the page is ready:

$(wowThisIsSoEasy); // aka. $(document).ready(wowThisIsSoEasy);

It allows you to arbitrarily add bits of rich functionality to your page with incredible ease. You can include little scripts that spruce up your pages from deep inside a nested partial. In general, people break the golden rule and include jQuery in the header. That is cause jQuery(document).ready is so awesome.

##The jQuery tax

There are 3 types of tax you pay when you have a script in your header. The constant tax and the initial tax and the refresh tax.

###The initial tax

The most expensive tax is the initial hit. Turns out more than 20% of the page views we get at Stack Overflow involve an non-primed cache and actual fetching of JavaScript files from our CDN. These are similar numbers to the ones reported by Yahoo years ago. The initial hit can be quite expensive. First, the DNS record for the CDN needs to be resolved. Next a TCP/IP connection needs to be established. Then the script needs to be downloaded and executed.

Browsers these days have a pack of fancy features that improve performance including: HTTP pipelining, speculative parsing and persistent connections. This often may alleviate some of the initial jQuery tax you pay. You still need your CSS prior to render, modern browsers will concurrently download the CSS and scripts due to some of the above optimisations. However, slow connections may be bandwidth constrained. If you are forced to download multiple scripts and CSS prior to render, they all need to arrive and run prior to render starting.

In the image below (taken from Stack Overflow’s front page) you can see how the screen rendering could have started a 100 or so milliseconds prior if jQuery was deferred - the green line:

An important note is that it is common to serve jQuery from a CDN be it Google or Microsoft. Often the CDN you use for your content may have different latency and performance to the CDN serving jQuery. If you are lucky Microsoft and Google are faster, if you are unlucky they are slower. If you are really unlucky there may be a glitch with the Google CDN that causes all your customers to wait seconds to see any content.

The refresh tax

People love clicking the refresh button, when that happens requests need to be made to the server checking that local resources are up to date. In the screenshot below you can see how it took 150ms just to confirm we have the right version of jQuery.

This tax is often alleviated by browser optimisations, when you check on jQuery you also check on your CSS. Asynchronous CSS is risky and may result in FOUC. If you decide to render CSS inline you may avoid this, but in turn have a much harder problem on your hands.

The constant tax

Since we are all good web citizens (or at least the CDNs are), we have expire headers which means we can serve jQuery from our local cache on repeat requests. When the browser sees jQuery in the header it can quickly grab it from the local cache and run it.

Trouble is even parsing and running jQuery on a modern computer with IE8 can take upwards of 100 milliseconds. From my local timings using this humble page on a fairly modern CPU i7 960, the time to parse and run jQuery heavily varies between browsers.

Chrome seems to be able to do it under 10ms, IE9 and Opera at around 20ms and Firefox at 80ms (though I probably have a plugin that is causing that pain). IE7 at over 100ms.

On mobile the pain is extreme, for example: on my iPhone 4S this can take about 80ms.

Many people run slower computers and slower phones. This tax is constant and holds up rendering every time.

Pushing jQuery to the footer

Turns out that pushing jQuery to the footer is quite easy for the common case. If all we want is a nice $.ready function that we have accessible everywhere we can explicitly define it without jQuery. Then we can pass the functions we capture to jQuery later on after it loads.

In our header we can include something like:

window.q=[];
window.$=function(f){
q.push(f);
};

Just after we load jQuery we can pass all the functions we captured to the real ready function.

Why have we not done this yet at Stack Overflow?

Something that may seem trivial on a tiny and humble blog may take a large amount of effort in a big app. For one, we need to implement a clean pattern for registering scripts at the page footer, something that is far from trivial. Further more we need to coordinate with third parties that may depend on more than a $ function or even god forbid document.write for ads.

The big lesson learned is that we could avoided this whole problem if we started off with my proposed helper above.

We spend an inordinate amount of time shaving 10% off our backend time but often forget the golden rule, in general the largest bottleneck is your front end. We should not feel powerless to attack the front end performance issues and can do quite a lot to improve JavaScript bottlenecks and perceived user performance.

in larger websites the CSS usually dwarfs jQuery's download size, and if your site relies heavily on javascript it makes no sense to render anything before it can work. Good advice in general, just don't take it as absolute.

the mobile tax is pretty big though and it is constant, for us at Stack Overflow CSS is a bit smaller than jQuery, page rendering is fairly stable without any need for jQuery stuff to assist in render prior to document ready. It is a valid point that some big sites have huge CSS files. Though I would argue that relying on JS for initial render is awkward and risky.

Sanjay
over 6 years ago

In my case, I need JS for the page to be usable and also in css I have some big sprite images. I want to load sprite images after the JS is loaded. What should I do for that?
If i put JS in header, then nothing will appear on page till JS is downloaded. I dont want that.

This will remove q from your global object and will also allow you to have a single, nice function call to attach your queued functions. The code is not exactly tested and just hacked into this form, but it should work out.

nice, still prefer the method above that captures the callbacks in the closure, but this offers more comprehensive support

Tb
over 6 years ago

â€¦ interesting read, old subject in general, VERY misleading title. The problem described is very jQuery agnostic.

Cody_Allen
over 6 years ago

This is probably ignorance on my part, but the section â€œWhy is that jQuery synchronous script include stuck in the html â€˜HEAD' section?â€ didn't really seem to answer that question for me. I'm sure there is a good reason, but I don't understand why we are doing this run-around instead of just keeping ALL JavaScript in the footer. Then as long as we load jQuery first, we can just use the jQuery.ready method directly, right?

I love your solution for enabling $(handler), so I took it a step further and added support for $(document).ready(handler), $().ready(handler) and $(document).bind(â€œreadyâ€, handler). I explain my solution in this blog post that I published minutes ago: Safely Using .ready() Before Including jQuery

I put together what appeared to be the â€œrevised versionâ€ on jsFiddle (collected from the article and comment contributions from Daniel Baulig and Phil Parsons). I threw in some console calls showing how things actually play out for anyone interested in order of execution (only one quirk, but it makes sense).

Thanks for writing about this. Figuring out the â€œright wayâ€ to do javascript that is both performant and manageable is becoming so much more important as apps become more and more javascript heavy.

I'd be really interested to hear more about your thoughts on clean ways for registering scripts at the bottom of the page. Have you settled on an approach and just need to implement it? Or are still evaluating? Thanks

I've actually considered this pattern for jQuery myself. The biggest problem is that common jQuery plugins are not generally defined inside of a jQuery DOM Ready call. They are defined immediately when parsed and depend on a fully defined jQuery object.

The pattern you show with pushing to a static array is of course the asynchronous pattern that Google came up with for their Analytics JS API. It is very useful for third party JS APIs, and I really wish that Facebook and Twitter would adopt a similar interface for their JS APIs for their social features.

It should also be noted that as of 1.7 jQuery actually implements an asynchronous AMD pattern. jQuery will actually let you know when it has loaded now. People should really consider trying Require.js http://requirejs.org/

Finally, in many cases, it would be far smarter and far less of a headache to compile and minify your JS sources into a single file that can be loaded at the bottom of your page body tag.

I am trying to understand just why this solution is being proposed â€“ it seems to me, even when including in the footer, you just include jQuery before other scripts. What benefit is there in including other scripts before jQuery if they depend on its document ready function?

Either way, using a completely separate handler would be far superior than overwriting $ â€“ many plugins attach additional information direction to the jQuery object making this solution only slightly helpful in very specific use cases. If they attach to $ or jQuery, it would be blown away when the actual jQuery loads.

Additionally, if your scripts occur just before the closing

tag, then they shouldn't need document.ready anyway.

Was the point of this solution due to async scripts that might return at any time, possibly before jQuery? (Haven't played much with async scripts like that, I just include scripts in the footer.)

Migration from a system with inline js sprinkled through the pages to one where all the scripts are in the footer. In general, deferring external includes (including plugins) is fairly simple compared to deferring inlines from what I have seen in both rails and asp.net mvc. From all my uses of jQuery I always capture functions behind document ready prior to interacting with jQuery.

Cases where you are defer loading jQuery itself, stubbing $ is perfect there, cause you still get a very clean and consistent API, even while jQuery is loading.

It is possible a separate handler would work better for your specific use case, it is possible that you may not need the hack at all if you simply push stuff to the footer.

I am unclear about the need for document.ready (or not) if you are the last script on the page, you would have to test this on multiple browsers to get a definitive answer.

I would argue that registering all your nice plugins with a $(selector).yourFunkyPlugin() for every plugin is problematic. We developed a nicer way to register your â€œpluginsâ€ in a light weight way, and executing the plugin logic only if a match for the selector really exists. It's called jsb, comments appriciated: https://github.com/DracoBlue/js-behaviour.

So why don't you use ControlJS, script.js or also popular LABjs to do the routine of loading other scripts in the desired order?

Placing the loader in the

section (it's only around 5kb without gzip and around 2kb with gzip!) and not having to rearchitect your entire system (the footer include pattern you mentioned) sounds like a really good compromise, no? I'm really curious to know why it was discarded?

Dmitry_Pashkevich
about 6 years ago

Oh, the HTML got trimmed, I meant â€œPlacing the loader in the HEAD sectionâ€¦â€.

I'm still stumped why would I need this and is it a real problem? I've been working in a commercial web development environment for the last more or less 5 years and I don't think I ever needed to put jQuery after any of the libraries I wrote? There is one great solution to this issue I guessâ€¦ The one I've been using for a long timeâ€¦ Use vanilla JS.

I’m loading jquery using the DEFER-attribute to speed things up even more. But I can’t figure a way to run this $.each(q,function(i,f){$(f)}) part after jQuery has been loaded. How do I tell when a “defered” script has been loaded? Tried googlin, found nothing :(

Dave Sumter
over 4 years ago

We could leave out the reference to window because we are in global scope, right…?

One of them is jquery. In the mean time, even before thinking about asynchronous, since I’m using this jquery responsive navigation menu http://www.smartmenus.org/, I noticed that setting jquery at bottom of the page, makes the jquery menu to doesn’t display the nested li(s)

Can you kindly hint with a working solution? Also think about many hosting are slow, so loading jquery from a CDN is still quite a must.

Thank you for a working/full example thought for not-js-professionals

Obviously the working example is meant as example with that jquery menu or modal popups lightbox-like (showing pictures or embedding youtube videos), accordions, sliders etc