CodeProject
Looking through my 'client side performance glasses' when browsing the web, I see that many sites spend too much time downloading resources, mostly on the homepage, but sometimes the main bulk is on subsequent pages as well.
Starting to optimize
When trying to optimize your page, you might think that it's most important that your landing page is the fastest since it defines your users' first impression. So what do you do ? You probably cut down on all the js and css resources you can and leave only what's definitely required for your landing page. You minimize those and then you're left with one file each. You might even be putting the js at the end of the body so it doesn't block the browser from rendering the page, and you're set!
But there's still a problem
Now, your users go onto the next page, probably an inner page of your site, and this one is filled with much more content. On this page you use some jquery plugins and other frameworks you found useful and probably saved yourself hours of javascript coding, but your users are paying the price...
My suggestion
I ran into this same exact problem a few times in the past, and the best way I found of solving this was to preload the resources on the homepage. I can do this after 'page load' so it doesn't block the homepage from rendering, and while the user is looking at the homepage, a little extra time is spent in the background downloading resources they'll probably need on the next pages they browse.
How do we do this ?
Well, there are several techniques, but before choosing the right one, lets take a look at the requirements/constraints we have -
- We want download js/css files in a non-blocking way
- Trigger the download ourselves so we can defer it to after 'page load'
- Download the resources in a way that won't execute them (css and js) (This is really important and the reason we can't just dynamically create a '<script/>' tag and append it to the '<head/>' tag!)
- Make sure they stay in the browser's cache (this is the whole point!)
- Work with resources that are stored on secure servers (https). This is important since I would like it to preload resources from my secured registration/login page too if I can.
- Work with resources on a different domain. This is very important since all of my resources are hosted on an external CDN server with a different subdomain.
The different techniques are (I have tested all of these, and these are my notes)
1. Creating an iframe and appending the script/stylesheet file inside it
var iframe = document.createElement('iframe');
iframe.setAttribute("width", "0");
iframe.setAttribute("height", "0");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("name", "preload");
iframe.id = "preload";
iframe.src = "about:blank";
document.body.appendChild(iframe);
// gymnastics to get reference to the iframe document
iframe = document.all ? document.all.preload.contentWindow : window.frames.preload;
var doc = iframe.document;
doc.open();
doc.writeln("");
doc.close();
var iFrameAddFile = function(filename) {
var css = doc.createElement('link');
css.type = 'text/css';
css.rel = 'stylesheet';
css.href = filename;
doc.body.appendChild(css);
}
iFrameAddFile('http://ourFileName.js');
This works on Chrome and FF but on some versions of IE it wouldn't cache the secure resources (https).
So, close, but no cigar here (at least, fully).
2. Creating a javascript Image object
new Image().src = 'http://myResourceFile.js';
This only works properly on Chrome. On FireFox and IE it would either not download the secure resources or download them but without caching.
3. Building an <object/> tag with file in data attribute
var createObjectTag = function(filename) {
var o = document.createElement('object');
o.data = filename;
// IE stuff, otherwise 0x0 is OK
if (isIE) {
o.width = 1;
o.height = 1;
o.style.visibility = "hidden";
o.type = "text/plain";
}
else {
o.width = 0;
o.height = 0;
}
document.body.appendChild(o);
}
createObjectTag('http://myResourceFile.js');
This worked nicely on Chrome and FF, but not on some versions of IE.
4. XMLHttpRequest a.k.a. ajax
var ajaxRequest = function(filename) {
var xhr = new XMLHttpRequest();
xhr.open('GET', filename);
xhr.send('');
}
ajaxRequest('http://myResourceFile.js');
This technique won't work with files on a different domain, so I immediately dropped this.
5. Creating a 'prefetch' tag
var prefetchTag = function(filename) {
var link = document.createElement('link');
link.href = filename;
link.rel = "prefetch";
document.getElementsByTagName('head')[0].appendChild(link);
}
prefetchTag('http://myResourceFile.js');
6. 'script' tag with invalid 'type' attribute
// creates a script tag with an invalid type, like 'script/cache'
// I realized this technique is used by LabJS for some browsers
var invalidScript = function(filename) {
var s = document.createElement('script');
s.src = filename;
s.type = 'script/cache';
document.getElementsByTagName('head')[0].appendChild(s);
}
invalidScript('http://myJsResource.js');
This barely worked in any browser properly. It would download the resources, but wouldn't cache them for the next request.
Conclusion
So, first I must say, that given all the constraints that I have, this is more complicated than I thought would be at first.
Some of the techniques worked well on all of the browsers for non-secured resources (non SSL) but only on some browsers for secured resources. In my specific case I just decided to go with one of those, and figure that some users will not have cached resources that are for SSL pages (these are a minority in my case).
But, I guess that given your circumstances, you might choose a different technique. I had quite a few constraints that I'm sure not everyone has.
Another thing worth mentioning is that I didn't test Safari on any technique. Again, this was less interesting for me in my case.
I also didn't think about solving this problem on mobile devices yet. Since mobile bandwidth is also usually much slower I might tackle this problem differently for mobile devices...