Load JavaScript and CSS with one request 23 January, 2013

pagespeedWhen it comes to web pages I’m a bit of a speed freak. I’m constantly on the lookout for new techniques to make my pages faster. So it’s a great pleasure that I can now say that I came up with a new* technique to speed up page rendering.

The idea came to me while I was analysing one of my pages and trying to figure out how to reduce the number of requests. I asked myself: “would it be possible to, somehow, combine CSS and JavaScript into one file?”. And the answer, it turns out, is yes!

I started hacking and soon had two proof of concepts (and quickly after that a name: platypus.js). The first one used AJAX to load & inject a CSS file into the page while extracting & injecting JavaScript that was inside a CSS comment. It worked, but there was a noticeable FOUC even on a page with just a few elements and very little CSS. This might be avoided by not using jQuery and using raw JavaScript to create the AJAX request (it’s on my TO-DO list).

The second concept proved to be more promising. I started researching if it is possible to access raw CSS files (that are loaded normally by the browser) and extract JS that way (thus avoiding AJAX), but sadly this isn’t possible. So I figured I might sneak JS code as a value of a CSS attribute. That proved possible and I used the `content` attribute which can accept arbitrary data. So once the CSS get’s loaded I read the value of `content` and inject JS code into the page. The next step was base64 encoding the JS code to avoid problems with quotes.

So the concept works but I still have a lot of work to do. Right now I’ve only tested this with Chrome. Once I test other browsers and make some benchmarks I’ll create a generic solution that anybody could use.

Until then, you can check the demos here, or view&fork the source code on GitHub here.

* I looked on the internet and haven’t found anything like this, but it is possible that my google-fu sucks and somebody else totally did this already

If you wish to discuss this post please use twitter or contact me.

Comments

Davide January 25, 2013 at 1:08 am

Hey Jan,
Glad to see people hacking around; coming up with new ideas is always a good thing, no matter how realistic they are (the important here is to stretch our imagination).
Btw, the approach you describe has not been done by anyone else in the past, and I believe it’s for a good reason: it doesn’t help…
Mixing CSS and JS is a bad idea IMHO, for many reasons:
- CSS Is loaded in parallel by the browser, while JS is not;
- CSS is loaded in the head (before the DOM is rendered), while JS is generally loaded generally in the body for performance reasons
- Minification of CSS and JS works differently, so including one into another is looking for troubles..
- In a decent sized application, with a moderately big codebase, putting everything into 1 single file could end up with an unmanageable big file; loading it in 1 single chunk would prove unpractical; either you end up with FOUC or you get a white page until the file complete loading by your browser..
Moreover, loading 1 single minified CSS file and 1 single minified JS file is generally more than enough in terms of optimisation..
I’m not even considering that recent talks/rumors at Google seem to indicate that modern browsers are nowadays extremely optimised and performant also when downloading multiple resources (it was an Angular guy that glanced over this concept in a recent screencast..)
Btw, don’t give up trying; Crazy Ideas can sometimes lead to great discoveries ;)

Cheers,
Davide

Jan Hančič January 25, 2013 at 8:15 am

Hi Davide,

thanks for the great comment!
I’ll try to address some of the problems you highlighted:

“- CSS is loaded in the head (before the DOM is rendered), while JS is generally loaded generally in the body for performance reasons”
CSS is still loaded in the head, and JS is executed after the whole document is rendered.

“- Minification of CSS and JS works differently, so including one into another is looking for troubles..”
Would you mind sharing some of the problems? I can’t seem to think of any :)

“- In a decent sized application, with a moderately big codebase, putting everything into 1 single file could end up with an unmanageable big file;”
Off course, I agree completely. But I never imagined that somebody using platypus.js would manually write CSS and JS in a single file (S is base64 encoded after all). The idea is rather that this would be handled automatically by your deploy process, so as far as the developer is concerned everything is still separated.

” loading it in 1 single chunk would prove unpractical; either you end up with FOUC or you get a white page until the file complete loading by your browser..”
So far I haven’t experienced this. This test (http://janhancic.github.com/platypus.js/experiments/index5_big.html) successfully loads a whole bootstrap demo which is quite big. But I admit that it’s entirely possible that there would be FOUC on even larger pages, I’ll have to investigate this some more.

“Moreover, loading 1 single minified CSS file and 1 single minified JS file is generally more than enough in terms of optimisation..”
Yes! Like I said this is a expiriment:) But still, if developer’s life isn`t complicated by this you still one request which is a win :)

Again thanks for the comment you raised some valid points!

Davide January 25, 2013 at 9:38 am

You know what they say.. curiosity kill the cat.. Gosh, now I’ll end up being hooked up to your crazy idea LOL ;)

Ok, I’ve seen your example; It’s much more clear what your approach is now. :)

My (potential) concerns are twofolds:
- performance
- flexibility (see point on AMD)

When I was talking about loading CSS/JS in header/body what I meant is that, putting your Base-64 encoded JS withing the CSS (in the head), you’re de-facto loading “everything” in one single place (the head), thus reducing the potential advantage of progressive rendering (CSS takes more time to load because of the JS payload..). It’s purely speculative, of course, but we’re thinking about all the possible consequences here, so a bit of stretching is allowed :)

About minification: how do you handle CSS and JS minification in this scenario ? you generally work in a dev pipeline, where your code is going to be precompiled (CSS) and minified (CSS and JS). The point of attention here is that you’ll need to ensure that your JS minification is happening before the code is actually b64 encoded and inserted into the CSS. Useful to keep in mind.

Your example seems to work quite well on chrome but it doesn’t work on Firefox.. Don’t ask me why, but on Firefox the b64 string starts with a double quote instead of a single one! just changing:
if ( js[0] === “‘” ) {…}
with
if ( /^["']/.test( js[0] ) ) {…}
should solve your problem on Firefox :)

One thing though: Have u thought about the impact of this approach on AMD ? this approach, although very interesting, seems to defeat the purpose of AMD.. (if you put all your JS into one or more compiled CSS files you are, de facto, loading everything in advance, defeating the purpose of lazy loading resources on demand..)

Of course, I understand that a single approach cannot fit all the bills (and probably shouldn’t..) and this could be nevertheless a useful solution in all those situations in which the size of your codebase is not so high to demand AMD solutions (like in common/corporate websites, landing pages etc..)

Cheers
Davide

Jan Hančič January 25, 2013 at 9:48 am

Yeah I still need to do the benchmarks. Then I’ll see if your concerns are valid or not (I’ve been thinking the same thing).

Thanks for the FF tip, I haven’t really bothered fixing stuff in other browsers yet, it’s on my TODO :)

As for the deployment, for now that exercises is left for the reader :) But I imagine it would be possible to create different solutions for different frameworks.

Haven’t thought about AMD. You can still split stuff into multiple files if you want. I usually have a common.css&common.js and page_specific.css&page_specific.js … you could still use platypus.js in this case and have 2 requests instead of 4.

Mitja January 25, 2013 at 12:13 pm

Really original concept, but I don’t think is the right way. We need to do more about caching assets in the browser. For example http://addyosmani.github.com/basket.js/ is awesome concept – basically caching javascript to localstorage and avoiding consecutive requests, which is what Google and Bing are already doing (for javascript).

Regards,
Mitja

Jan Hančič January 25, 2013 at 12:16 pm

Yeah basket.js looks interesting. Maybe the same concept could be used for CSS.

Alex Gorbatchev January 25, 2013 at 6:13 pm

Hey hey :) sounds like i had the same idea a while back, check out http://bundlejs.com/ I also have a jquery component which is distributed in exactly this way – JS & CSS in one file http://textextjs.com :)

Jan Hančič January 25, 2013 at 6:17 pm

You are putting CSS into JS whereas I’m putting JS into CSS :)

Cool project never the less, I’ll check it out later in more detail.

Nikolay Nemshilov January 25, 2013 at 11:12 pm

@Jan rightjs and lovely.io inject CSS into JS by default for 4 years now :))

Jan Hančič January 25, 2013 at 11:17 pm

Again. This is injecting JS into CSS, not the other way around. Thanks for the commet though.