In real life web projects, you won’t write much client side code without the help of a Javascript framework. They just make your life too easy to ever consider going without one, except for the most trivial of tasks. For lots of people, myself included, the framework of choice is jQuery, so that’s what I’m writing about here, but the general principles I’m going to describe apply to all of them.
So, what’s the deal? You just drop it in the <head>
and forget about it, right?
Right, that’s the basic way to do it:
1 | <script type='text/javascript' src='jquery.js'></script> |
Since jQuery is in active development, and there’s a new version of it coming out every now and then, it probably will look more like this:
1 | <script type='text/javascript' src='jquery-1.6.4.js'></script> |
But you can do better than that.
Fun-Sized
First of all, when you download jQuery, you have a choice to make: regular-sized (uncompressed) or minified. It’s pretty obvious what those versions are aiming at:
The minified version has a much smaller file size, which is accomplished by various methods, like stripping out whitespace and comments, and shortening variable names, which renders the whole thing basically unreadable for any human being. Once your website has gone live, you will want it to load as fast as possible, so that’s where this squeezed down version comes in.
There are good reasons not to use it in development, though: Your code relies on jQuery to do the grunt work, and it’s easily possible that a mistake you make goes unnoticed during the time your own code does its magic, but starts raising a stink when jQuery suddenly doesn’t have what it needs in order to actually do what you were asking. In that case, your debugger will tell you that an error happened in some part of the jQuery code, but you can’t read any of that boiled down poppycock, and what’s worse, you don’t even know where to start with any deciphering ambitions, since the line number the debugger is pointing to is probably something like 1 — stripping out whitespace means that the whole code ends up in just a handful of absurdly long lines. You can have a look at the stack trace to see where the error originated, but it’s just that much easier to actually see what’s going on at all times, so for development you definitely want regular-sized.
Fun Fact: If you take a character count of the last two paragraphs, you will find that the second one is 2.598 times longer than the first one. Just to give you a visual aid, that’s exactly the ratio of the regular sized to the minified file of the current version of jQuery (1.6.4).
Okay, two versions for two environments, that’s easy enough. Be careful, though, not to get yourself into a bind here: you don’t want to find yourself in a situation where you have to make changes to your code every time you deploy it to the server. Ideally, you will want to be able to just dump your code on the server, where it should figure out on its own that some things have to be done differently now.
Since the script tag that loads jQuery sits right in your HTML, and your HTML comes from the server, that’s where you will have to check which environment is applicable. I’ll just assume that you are using PHP here. Drop this line at the beginning of the PHP script that’s generating your pages, and you will have an easy way to check whether the script is running on your own machine or on the server:
1 2 3 | // The constant PRODUCTION will be true whenever the script // is not running on your local machine define('PRODUCTION', $_SERVER['SERVER_NAME'] !== 'localhost'); |
When it’s time to load up a jQuery file, you can use that constant to decide which one:
1 2 | <script type='text/javascript' src='jquery<?php PRODUCTION && print('.min'); ?>.js'> </script> |
You can use that constant for many other things too, of course, like different error reporting/logging/caching options in different environments, or for choosing the right database. It can also be helpful to know your environment inside your Javascript code — you can get that information there like this:
1 2 3 | <script type='text/javascript'> var PRODUCTION = <?php echo json_encode(PRODUCTION); ?>; </script> |
You could ditch the PHP and just use window.location
, of course, but it’s better to keep the logic that decides on the environment in one place.
Once it’s in there, you can use it, for instance, to disable any debugging output in your Javascript code, if it isn’t running locally:
1 2 | // Overwriting console.log with an empty function in production environment PRODUCTION && (console.log = function () {}); |
There are many applications for this, but if you only remember that checking out the environment enables you to always load up the right version of jQuery without ever having to change your code, I’m fine with that.
Skynet
Alright, we’ve got the thing set up to serve the right jQuery file for every situation. I hope you won’t be too devastated to hear that it’s doubtful whether we should serve it at all.
What’s the alternative? Since jQuery is so popular and has become somewhat of a de facto standard, there are several content delivery networks (CDNs) that provide fast public access to jQuery free of charge.
For instance, using Google’s CDN, the snippet from the very beginning could look like this:
1 | <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script> |
The environment-aware version of the code is also just the same as before:
1 2 3 | <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery<?php PRODUCTION && print('.min'); ?>.js'> </script> |
Is this the better choice than serving the file yourself? If you do a little research on the subject, you will find that there are rather heated discussions going on about that. What it comes down to is this:
The main pro: For people who already have the CDN’s file of the jQuery version you are using in their cache, the site will load faster. Notice here that just because site A and site B are serving the exact same version of jQuery, that doesn’t mean that a user navigating from site A to site B won’t have to load it there a second time. The browser doesn’t know that it’s actually the exact same file until it has fully loaded it, and then it’s too late. If both sites are using the same CDN, though, when visiting site B after site A it can just be fetched from the cache, since it’s coming from the same place, so the browser already knows it’s the same thing.
The main con: You are losing control. Google’s servers might be blocked in some countries, and, though improbable, it’s possible (and has happened) that they are unavailable. In those cases your site won’t work the way it should, even though it’s perfectly reachable itself. If you’re a conspiracy theorist, you might also enjoy contemplating whether Google might become Skynet any moment now and start inserting their own code into the jQuery file you are getting from them, but that’s a bit far-fetched.
My take on this is the following: By now, enough sites use Google’s CDN for serving jQuery that a large number of people will already have it cached, so it makes sense to go that way yourself. It’s only reasonable to implement some sort of fallback, though, in case someone really does have problems accessing it that way.
That can be done like this (assuming you are using an HTML (as opposed to XHTML) doctype, which you should be doing anyway):
1 2 3 4 5 6 7 | <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js'> </script> <script type="text/javascript"> window.jQuery || document.write('<script src="jquery-1.6.4.js" type="text/javascript"><' + '/script>'); </script> |
It will check whether jQuery is loaded, and if it isn’t, it will write out an extra <script>
tag that includes your local copy.
Notice how I wrote window.jQuery
instead of just jQuery
. Global variables are stored as properties of the window object, so if jQuery is actually already loaded, those two will be the same. If it isn’t, though, trying to access the undefined property window.jQuery
will just give you null, which is ok, but trying to access the undefined global jQuery
will throw an error.
Also notice how I wrote '<' + '/script>'
there instead of just '</script>'
— that’s because if the browser sees '</script>'
in there, it thinks that’s the end of your actual script, which it isn’t.
Writer’s Block
Another thing, that’s not quite on topic, but important: Many beginners’ tutorials dumb down the code they are using to something you would never ever use in real life. A perfect example of this is document.write
. Many tutorials use it for any sort of output, because it’s so easy to understand, but the truth is that in real life you need a very good reason to use it at all. It’s reasonable to put all your scripts in one place, so when the time comes to make a change, you know immediately where to look — but document.write
writes something into your HTML at the exact place you put the command, so in order to do anything remotely sensible with it, you are forced to put your scripts into all kinds of places they don’t belong. That’s a Bad Thing, and it makes the site really hard to maintain in the long run. Also, once a page is loaded, document.write
will just delete everything before writing.
Usually, when bad tutorials use document.write
, they mean to do one of two things: write something that actually belongs into the page (content), or write something just for the learner’s benefit, so they know what’s going on (debugging information). In real life, you would use DOM manipulation for the first, and console.log
for the second.
That said, here’s the reason I used document.write
here: Since that script tag is definitely something that belongs into the page, and not just debugging information, after what I just said you would use DOM manipulation to add it, and that might look something like this (I found that exact piece of code suggested somewhere else):
1 2 3 4 5 6 7 8 9 10 11 | <!-- DON'T DO IT LIKE THIS! --> <script type="text/javascript"> if ( ! window.jQuery) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'jquery.js'; var scriptHook = document.getElementsByTagName('script')[0]; scriptHook.parentNode.insertBefore(script, scriptHook); } </script> |
The problem is one of execution order: If you include your script like this, it will actually run after all of the other scripts have run, so jQuery will be loaded only after the scripts that rely on jQuery already being there have tried to run and failed. That won’t do you any good.
The Gist
So, to sum this up, use the minified version of jQuery in production, but not in development, and use Google’s CDN for serving it, but implement a fallback.
All in all, the code to accomplish all of that looks like this:
At the beginning:
1 | <?php define('PRODUCTION', $_SERVER['SERVER_NAME'] !== 'localhost'); ?> |
In the <head>
:
1 2 3 4 5 6 7 8 | <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery<?php PRODUCTION && print('.min'); ?>.js'> </script> <script type="text/javascript"> window.jQuery || document.write('<script src="jquery-1.6.4<?php PRODUCTION && print('.min'); ?>.js" ' + 'type="text/javascript"><' + '/script>'); </script> |
Afterthought
You might have heard that by not putting your scripts inside the <head>
, but just before the closing </body>
tag, you can make your site load faster. This is (partially) true: The actual content will be loaded before the scripts, so it will show up earlier. Overall, the load time will be the same, of course, but you can give your visitors something to read while they are waiting for the magic to kick in.
You can certainly do it that way, but there are some downsides, which might or might not affect you:
First, are you sure you want to show your content before your scripts are up and running? If all your code does is add a few minor enhancements, the answer might be yes. There’s no reason to make your visitors wait for jQuery to be loaded, just so you can make sure your tooltips are all set up before showing anything. If, on the other hand, your site heavily relies on your scripts for presentation or function, this might be a problem: If it’s about presentation, you will have a FOUC (Flash Of Unstyled Content) on your hands, which means that what a visitor sees before your scripts kick in differs significantly from what it looks like afterwards. This can be annoying. If it’s about function, you are giving your visitors a chance to interact with the site before your custom interactions are actually set up, which leads to the next point:
Second, are you sure you don’t need to do anything before the content actually loads? Very often, the answer to that is an easy yes, because you’re waiting for the DOM ready event anyway, before doing anything. Maybe you’re setting up a jQuery.live
event, though, in order to catch clicks on links before the whole content is loaded, in which case a script at the end of the <body>
won’t do you any good.
So, all in all, it comes down to this: It’s a good thing to keep that option in mind, but you will have to judge on a case-by-case basis whether it’s feasible. Putting jQuery in the <head>
, on the other hand, is fool-proof.