1

I have a general question about the placement of javascript code in a AJAX-driven web application. At a previous job, we tended to throw everything in one monolithic file. When maintaining that file became too difficult, we split it up into multiple smaller files and "glued" them back together with a php "loader" (pretty much just a bunch of include statements)

Since all of my pages are dynamically loaded through AJAX, I cannot simply have different files called separately per page.

The obvious disadvantage to this method is that all of your javascript is loaded by the end user, even if it is not required on first page load. (Which compounds the problem, the more pages you have)

To get around this, I started putting page-specific javascript on the template page itself, in a <script> tag at the bottom, which works as intended. Only the javascript I want is loaded.

However, I am unsure of whether this practice is taboo, or if there are better strategies for loading my javascript. I get the feeling that mixing my template with my scripted code is a recipe for disaster..

6
  • "Since all of my pages are dynamically loaded through AJAX" Do you do any SEO ?
    – Raynos
    Oct 14, 2011 at 13:41
  • Funny you ask... I implemented history.js just this week. So... yes? Oct 14, 2011 at 13:44
  • Wait, what? How does an extra javascript file you support non-javascript bots and crawlers?
    – Raynos
    Oct 14, 2011 at 13:45
  • I mean to say that all of my pages are indexable, but not exactly accessible to an end-user who has js turned off. As it's integral to the function of the app, I'm willing to ignore the ~10% of users who choose that route. Oct 14, 2011 at 13:48
  • Raynos is saying that 10% includes all search engines. Oct 14, 2011 at 17:50

2 Answers 2

4

Short answer: One monolithic file with a far-future expires header.

Long answer: It depends. If you're Gmail and have over 1 megabyte of javascript in your application, then you're going to want to split that up to reduce load and improve user experience. For the rest of us though, the best solution I've found is to load your main library (such as jQuery) from a third-party CDN (like Google). Then split up your one monolithic file into two: lib.js and app.js. Lib should contain any third-party libraries, and app your site-specific code. The general idea being that you're very rarely going to be updating libraries or adding new ones, so that file will rarely ever have to be cache invalidated and re-downloaded by your users.

Use a technique such as the one described on this site to move almost all on-page <script> tags without a src attribute to an external file:

http://paulirish.com/2009/markup-based-unobtrusive-comprehensive-dom-ready-execution/

So what you should be left with are <script> tags with a src, and the only inline ones remaining should be because you need to pass data from your backend code to javascript (and want to avoid an AJAX request on page load that will block rendering). Say the namespace of my project is Foo, then that one inline tag will look like:

<script type="text/javascript">
    Foo.Context = {{ js_context|safe }};
</script>

js_context is a variable I passed to my template from my backend, and is already a JSON object. The squiggly brackets and |safe are just from the front-end template engine I happen to be using (in this case, Django's built-in templates). Now my page-specific code, living in app.js, can look at the contents of Foo.Context after document.ready fires for any variables it needs while avoiding an initial AJAX request.

As usual for your static files, you should be serving them with far-future expires headers:

Cache-Control: public, max-age=31536000

When you update your scripts, your build step should either rename the concatenated and minified file or add a GET query parameter to the end of the <script> src, like so:

app.12345.js or app.js?v=12345

That way your users will request the new file, breaking the cache. As a caveat, please be aware that old versions of some reverse-proxies (such as Squid) don't play nicely with GET parameters on static files, so the latter of those two options wouldn't work. But I have never run into that issue yet, so your milage may vary.

When you do get to the Gmail level of script sizes, you can start looking at using a library like LABjs or RequireJS, to dynamically only load the pieces of app.js that you need for particular pages or functionality.

Last but not least, I hope your <script> tags are at the bottom of your page, right before the closing </body>. This prevents the browser from showing a blank white page while your scripts are downloaded, parsed, and executed. The only exception to this rule are libraries that absolutely must be in the <head> to function, such as Modernizr.

4
  • Good god man, that's a lot of detail. I'll be digesting this for hours. Too bad I can't upvote you more. Oct 14, 2011 at 17:50
  • 1
    Also, yes, my <script> tags are definitely at the end Oct 14, 2011 at 17:55
  • @JulianH.Lam Yeah, reading it now made me realize I might have tried to squeeze a bit too much into the answer. Oh well. Please feel free to toss me a message if you have further questions or need clarification on some bits of this. I know it must be a lot to take in. Oct 14, 2011 at 18:19
  • Right now, I am still skittish over having all of my javascript for all of my dynamically loaded pages contained in one monolithic file. As the two answers here suggest, this is probably a non-issue, but I just need to buck up and shake off the feeling. Paul Irish's article is amazing - it's a very elegant piece of code, and looks much better than what I have (or have had in the past). Oct 14, 2011 at 18:23
1

There are two main JavaScript loading techniques.

The big file

Some people say HTTP cache works so send all of that javascript in one packaged minified gzipped file and forget about the rest. The first one is expensive, the rest are cheap

The parallel small HTTP requests

Other people say but you can send multiple HTTP requests in parallel so you should send many small asynchronous HTTP requests using some kind of loader library like require.js

Which you use is upto you, personally I use one monolithic file and I use a sensible packager (browserify)

1
  • Thanks for the insight, Raynos. I've heard of using Google's hosted javascript libraries (to offload some of the requests to my own js library), and I suppose I can revisit the idea of a monolithic file :) Oct 14, 2011 at 13:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.