0% found this document useful (0 votes)
7 views82 pages

II Loadtime

The document discusses strategies for optimizing JavaScript load times, emphasizing the importance of caching, script load order, and reducing complexity. It highlights non-invasive techniques such as proper caching and file compression, as well as more invasive methods like reducing DOM complexity and hosting on faster servers. The authors encourage readers to purchase the book to support ongoing content creation and provide detailed guidance on implementing these strategies effectively.

Uploaded by

spimarzin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views82 pages

II Loadtime

The document discusses strategies for optimizing JavaScript load times, emphasizing the importance of caching, script load order, and reducing complexity. It highlights non-invasive techniques such as proper caching and file compression, as well as more invasive methods like reducing DOM complexity and hosting on faster servers. The authors encourage readers to purchase the book to support ongoing content creation and provide detailed guidance on implementing these strategies effectively.

Uploaded by

spimarzin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Part II

Loadtime
JavaScript Performance Rocks!
by Amy Hoy & Thomas Fuchs
[Link]
Although we do a lot of open source
work, this book is not free.

Why this book costs money & why you should pay
We want to share our hard-won expertise with the world for
a reasonable price so we can keep on creating free and
cheap content, code, and art for everyone to enjoy.

That “cheap and free content, code, and art” includes


[Link] itself, the hundreds of helpful and
entertaining blog posts & presentations, and other such
goodies that everyone benefits from.

All this boils down to a simple request, from us to you:

Please buy a copy if you’ve


received this copy without paying.
It costs the equivalent of three movie tickets or one dinner for two at Denny’s.
We know it’ll be worth more than that to your projects and your business.

OK, OK! I’ll buy a copy now...


Contents
Welcome to Loadtime 1
That page just takes forever! 1
Two types of loadtime laments 2
How we’ll fix it 3
Making the right choices for your app 4
Moving Forward 4
Script Load Order 5
The Cachét of Caching 7
Expiration Headers 7
Caching Strategies 9
Far future cache periods 9
Short cache periods 10
Configuring Apache 1.x and 2.x 12
Configuring nginx 14
Caching Gotchas: JSON & Generated JavaScript 15
Testing Your Cache Setup 15
Con-ca-te-nation 16
What to Concatenate 17
Concatenation Counter-indicators 17
Concatenating on Deployment 18
Your Custom Solution 18
Installing Sprockets 19
Using Sprockets 20
Running Sprockets 21
Setting Your Load Path 22
Using Sprockets with Web Frameworks 22
Other Fun Stuff in the Docs 24
Inlining & Precaching 25
Inlining Isn’t Evil 25
Pre-caching: A Beautiful, Sneaky Trick 27
Under Compressure 30
The Problems with Packing 30
Good Minification 32
Gee! Gzipping is the Answer 36
Google gzips So You Don’t Have To 38
The Fruits of Compression 41
Cover Your Assets 42
You Need an Upgrade 49
Out of Sight, Not Out of Mind 49
Catching Back-end Issues 50
Profiling Back-end Issues 50
Reduce Complexity 52
General Complexity 53
Watch out for Ajax 54
Browser-specific Complexity 54
JavaScript, On-Demand! 56
Inserting JavaScript on Demand 56
SSL & Serving JavaScript 58
Mixed Content Warnings 58
JavaScript & SSL & Caching 59
Smush Those Images! 60
Two Ways to Use Smush 61
Installing smusher 61
Using [Link] 61
Watch out for GIFs 62
Favicons Are Ridiculous 63
Making a Favicon 63
Referencing a Favicon 64
CSS Sprites are Magical 65
Creating Your Own CSS Sprites 66
How Sprites Work: Our Example 67
Creating your sprites file 68
Writing your CSS 69
Stop! Don't Close That Tag! 70
Omittable Closing Tags 71
No-Close Void Tags 72
Entirely Omittable 72
The Closing Tag—Er, Word 73
Screw IE & Invent Custom Tags 74
What? Inventing my own tags? 74
Chapter 1
Welcome to Loadtime
you’re going to spending a lot of time here. a whooole lot.

Welcome to loadtime, the magical in-between place


between your user’s browser’s HTTP request and the fully
downloaded, fully rendered page.

If you’re going to optimize, you should always start here. There’s tasty
low-hanging fruit, just waiting to be picked.

This is where the majority of performance issues occur.

Feel free to implement any or all of the suggestions here without the fear
of premature optimization. (Except for excessive minifying. You heard it
here first.)

That page just takes forever!


The worst offender in my book? eBay.
You know you’ve got a loadtime problem when it takes a long time for
They have millions of people literally
your page to “finish.”
fighting to give them money, but the
page takes 2-3 minutes to download
That means from the time your user clicks on a link or bookmark to the
and stop churning. Although
point where he can interact with everything without getting the spinning
GoDaddy’s pretty high up there, too.
pizza (or tilting hourglass) of death.
Welcome to Loadtime

Phases of loadtime include: Two types of loadtime laments


Loadtime slow-downs fall into two camps:
• connecting to your server
• transferring all related files (including remote download speed issues:
scripts) • pure “page weight”
• parsing and display the DOM • too many files
• rendering images • bad or no caching
• executing any JavaScript that needs to get run • slow servers (both soft-/hardware & bandwidth)
• unresponsive remote script hosts
Your users hate loadtime problems, because they want
to interact with your app already. That’s why they’re browser churning issues:
your users, after all. • complex DOM
• bad script load order
They don’t care where the problems occur or why, • slow computer
they just want to click stuff. • old browser

2
Welcome to Loadtime

How we’ll fix it


These are the types of remedies at your disposal for loadtime issues:

Non-invasive techniques (mostly):

• loading JS at the proper points


• appropriate caching
• reducing the number of files
• in-lining and precaching
• reducing file size
• increasing the number of possible simultaneous browser streams

And, more invasive but very effective techniques:

• reducing page / DOM complexity (cuts down churning and file


size)
• on-demand JavaScript

And one final technique, easy to say but not always fun to
implement:

• hosting on a faster server (and/or with better bandwidth)

3
Welcome to Loadtime

Making the right choices for your app


You want to do the least amount of work for the most pay-off, right?

Perfect. That’s the right attitude for tuning your JavaScript-heavy web
app.

The biggest gains can often be had in the “non-invasive” realm: especially
good caching and file compression. And they don’t involve tweaking your
code, not even a little bit—it’s a win/win situation.

We recommend you try the non-invasive techniques first, followed by


checking your DOM complexity.

You can also use our DOM Monster tool and YSlow to identify loadtime
trouble areas & beat them into submission.

Moving Forward
Several chapters follow this one, each featuring one of the techniques
we’ll be using to tune your loadtime experience. These chapters all begin
with ‘Loadtime.’

4
Chapter 2
Script Load Order
the easiest & most effective fix, EVER.
Fun browser fact: the browser won’t begin to render the
very HTML and CSS of your web app unless all JavaScript
files are fully loaded (or just about).
For the fastest, simplest, and easiest performance increase you can get...
put your <script /> tags at the very bottom of your <body /> tag.

Biggest Bang for Your Buck


Placing your script tags at the end won’t improve your objective,
measurable page load speed (bandwidth determines that).

But it does mean that the browser won’t stop rendering the page while
it waits for the JavaScript files to transfer and do their thing—drastically
improving the perceptual experience for your users.

Implementing this fix can take a first-load experience involving several


seconds of blank browser window churnage, and turn it into a page that
loads progressively. Sweet!

Especially for Remote Services


This is especially beneficial for external JavaScript sources like Google
Loadtime: Script Load Order

Analytics and other such third-party stuff.

When the server behind this external stuff (or the network between you
& them) is slow or unresponsive—which seems to happen regularly to
various stats services—your app will seemingly take forever to load. With
the script tags at the end, at least, your user won’t be stuck with a blank
screen.

Caveat: Progressive Functionality


The only real caveat with this trick is that the JavaScript functionality
of DOM elements won’t be available until the JavaScript files are fully
transferred and executed.

Makes sense, right? This is a rare side effect but it can occur.

Possible example: that autocomplete text field you have will appear
almost immediately, but not necessarily function right away. The strength
(or even presence) of this effect is completely determined by bandwidth
and browser speed. When everything’s snappy, it probably won’t be an
issue at all.

6
Chapter 3
The Cachét of Caching
So Good, It’s like Christmas for your Software
Caching should be the second tool you reach for, the
moment your application goes live.
Browsers have cached files automatically since the days of dial-up, but
they’re not the smartest cookies in the cookiejar.

Browsers don’t keep caches very long or necessarily use them in an


intelligent manner.

Luckily, they do respond to commands—you can easily configure your


web server or server-side scripting language to order the browsers
around, and tell them to keep the right files for longer.

Expiration Headers
Specifically, you want to set the expiration headers on your content files
that don’t change often—CSS and, of course, JavaScript files. Server Access Required!
These techniques require you to
The goal is to have the browser keep those copies of these files until have access to your web server
they change, so that the latest version is always right there on the user’s configuration. So get to it!
computer. Or order your hosting company to do
it for you.
This single change can save tens of time-wasting requests per page view.
Loadtime: The Cachét of Caching

The Final Result


You want your finished headers to look something like this:

Date: Tue, 22 Jul 2008 19:08:42 GMT


Expires: Wed, 22 Jul 2013 19:08:42 GMT
Cache-Control: max-age=157680000

In this case, the file was downloaded on July 22nd 2008 and will be held
until July 22nd, 2013—5 years in the future—at the minimum.

The browser will then check to see if the file has changed and, if so,
download the new version. If no changes have occurred, it’ll keep happily
humming along with the cached copy.

The last line is what makes it happen:

Cache-Control: max-age=157680000

The Cache-Control header takes an argument of max-age in seconds;


157680000 seconds is 5 years (60x60x24x365x5).

8
Loadtime: The Cachét of Caching

Caching Strategies
There are two major strategies for caching JavaScript and CSS assets:

• very long cache periods (e.g. months or years; “far future cache”)
• short or medium cache periods (days) (“short cache”)

They’ve both got their pros and cons, and, unfortunately, both require a
different support system to work right.

For sake of argument, let’s pretend we’ve got this interesting little file
that we want to cache and it’s called our_app.js.

Here’s how it’d work with both strategies.

Far future cache periods


With a far-future caching setup, you set the expiration date far in the
future (big surprise)—months or years. That’s a long time.

This seems ideal, because then your user’s caches will be safe and their
experience will be snappy until many happy months go by.

But meanwhile, back at the data center, you want to roll out your spiffy
new psychic autocompleter—but nobody will know because you originally
set a far-future cache expiration date to 2010.
9
Loadtime: The Cachét of Caching

If you’re going to do far-future caching periods, there’s just one way to


distribute new code & content: change the filename.

The browser will say “Hey, I’ve got [Link] but not [Link] in my
cache, it must be new” and slurp it down fresh.

Yahoo! does this by including a version in their JavaScript filenames:

ourapp_2.[Link]

Ruby on Rails does this automatically by adding a query string to the end
of the base filename, that reflects the last modification date of the file:

[Link]?20080822

You can take whichever approach you like, or think up your own.

As long as you change the filename when you need to push changes to
your users, far-future caching will work for you.

Short cache periods


With short caching periods, the “expiration date” on your cached files
comes up much sooner.

When that happens, your user’s browser will ping the server for the 10
Loadtime: The Cachét of Caching

Last-Modified header on the file.

If the date’s different than the copy it’s already got saved, the browser
will download it fresh.

The browser will always check for the same file name. If you cache
[Link] with a 24-hour maximum age, the browser will ask for the
headers for [Link] every 24 hours. Rinse and repeat.

You should stick to far-future caching unless you don’t have control of
your app’s filenames.

If you can’t change the filename, try the short-cache approach.

11
Loadtime: The Cachét of Caching

Configuring Apache 1.x and 2.x


It’s easy to configure Apache to send the correct caching headers using
the built-in ExpiresActive extension.

Add the following (version appropriate) code to your conf file if it’s not
already there.

First, you need to load and activate the module:

Loading the Module in Apache 1.3


LoadModule expires_module libexec/mod_expires.so
AddModule mod_expires.c

Loading the Module in Apache 2.0


LoadModule expires_module modules/mod_expires.so

Be sure to place the above snippet inside the correct block for the
domain name/application you’re configuring.

12
Loadtime: The Cachét of Caching

Far future caching (5 years)


ExpiresActive on
ExpiresByType text/css “access plus 5 years”
ExpiresByType application/x-javascript “access plus 5 years”
ExpiresByType text/javascript “access plus 5 years”
FileETag none

This sets all items to 5 years.

Short cache horizon (24 hours for JS, 5 days for CSS):
ExpiresActive on
ExpiresByType text/css “access plus 5 days”
ExpiresByType application/x-javascript “access plus 24 hours”
ExpiresByType text/javascript “access plus 24 hours”
FileETag none

13
Loadtime: The Cachét of Caching

Configuring nginx
If you’re using nginx, it’s simply a matter of putting this nice concise
snippet inside your server{} configuration block:

Far future caching (5 years for all CSS & JavaScript)


location ~* \.(js|css)$ {
if (-f $request_filename) {
    expires 5y;
    break;
  }
}

Short cache horizon (24 hours for JS, 5 days for CSS):
location ~* \.css$ {
if (-f $request_filename) {
expires 5d;
break;
}
}

location ~* \.js$ {
if (-f $request_filename) {
expires 24h;
break;
}
}
14
Loadtime: The Cachét of Caching

Caching Gotchas: JSON & Generated


JavaScript
Be careful not to cache things you don’t want to cache—namely data and
generated JavaScript that’s customized to your users.

The easiest way to avoid this problem is to give your JSON and
generated JavaScript code a different file extension.

We call our data files [Link], for example.

You might use a custom extension for generated code, too.

Testing Your Cache Setup


Don’t just set it & forget it—test it, too.

You can use the Safari, WebKit or Chrome Inspector, Firebug with
Firefox, or the Charles proxy to double-check that you’re sending out all
the right headers with your assets. And you can use YSlow (with Firebug,
with Firefox) to be sure that the caches are having the desired effect on
your page weight.

If you glossed through the “Is Your App Behaving Badly?” chapter, now’s
a good time to go back to it!

15
Chapter 4
Con-ca-te-nation
Fewer Files is Better.
Most web apps are composed of scores of little files. It’s
just convenient to write them that way, and it makes
it easy to find the code you’re looking for when you’re
developing.
Here’s another tasty low-hanging fruit: Reduce the number of files you
pump to the browser.

Your web app, like so many others, is undoubtedly composed of a million


little files.

Unfortunately, browsers (by default) only open 2 to 4 simultaneous


connections to the server, regardless of how many stylesheets, images,
HTM
L

IMG
:FE:LII<EK N8@K@E>

and JavaScript files are coming down the pipe. JS

CSS

The browser won’t start fetching the next batch of files until the current
batch are done.

Even worse, browsers can’t load JavaScript files in parallel: doing so


would potentially create procedural issues with interdependent code. Browsers only open 2 - 4 simultaneous
connections—see the Multiple Streams
chapter for more details
So, reduce the number of files the browser must download, and reap
a massive win. You probably already do this for images and CSS (and if
Loadtime: Con-ca-te-nation

you don’t, you should); doing it for JavaScript can only help.

The trick is to structure your files effectively for both development and
production environments: when you’re writing code, break out the files
however makes sense to you; when you push to the server, smush the
files together, preferrably into one file. Be mindful of the order of the
code in the file(s) is sane.

What to Concatenate
Your project probably has some combination of library files, original
code, and data in JSON or other JavaScript format.

This goes for all the files for any JavaScript libraries you may be using—
Prototype, jquery, mootools, YUI, [Link], & so on—as well as your custom
code.

Concatenation Counter-indicators
Concatenation isn’t always the answer. If the code is both A) weighty
and B) used only on a select few areas of your app, it may not be a good
candidate to roll into your monolithic JavaScript file.

For example, a date-picker might be a few hundred lines and yet only
used on one or two screens. In this case, you may choose not to have
it in the main file for the entire app; it may make more sense to include
it on those areas only, or load it dynamically in the case of a more 17
Loadtime: Con-ca-te-nation

interactive application.

Concatenating on Deployment
Deployment time is the best time for concatenation. And you don’t have
to do it by hand any more.

Your Custom Solution


You could roll your own solution to concatenate your JavaScript files on
deployment (or even per page request, with caching, although that’s less
efficient).

It’s not hard, really, with any scripting language that’s meant to be
embedded inside HTML (or other surrounding code). Just add yourself
some require() functions, or whatever the language of your choice uses,
and be sure they are parsed in the correct order.

But, as we just told you, you don’t have to do it by hand any more.

Sprockets is a lovely tool from the Prototype Core team, built specially
to smush all your JavaScript files together on command. With Sprockets,
you can split up your code into as many JavaScript files as you like, and
then Sprockets will join them up for you when you deploy your app—
either by hand, or with your deploy script.

And it works with all JavaScript code, not just Prototype.


18
Loadtime: Con-ca-te-nation

Bonus: Sprockets will handle all of the inter-file dependences for you.

Installing Sprockets
Sprockets is written in Ruby and distributed as a Ruby gem. But it’s MIT-
licensed and someone has also made a PHP version (see below).

To install the original Ruby version, you’ll need the Ruby language
environment and RubyGems, the package distribution system. If you’re
running on OS X, you already have both of these goodies. Otherwise,
you may need to install them first.

Once you’re prepped, install Sprockets with this command:

$ sudo gem install --remote sprockets

19
Loadtime: Con-ca-te-nation

Using Sprockets
Add dependencies to your JavaScript files, just like you would for Ruby or
any other language.

Sprockets’ syntax is super simple:

//= require <prototype>

Or…

//= require "prototype"

Or, for files in subdirectories…

//= require <lib/prototype>

The key difference between the two forms (quotes or angle brackets):

• angle brackets will search your load path iteratively for [Link]
and load the first one it finds;
• quote marks will attempt to load CURRENT_DIR/[Link]
immediately (no searching)

Say what? We know, it's confusing. Turn the page for a friendly example.

20
Loadtime: Con-ca-te-nation

Quotes or Brackets? A Friendly Example


So, let’s say your file tree looks like this:

javascript/
[Link]
lib/
[Link]

Quote marks won’t work because the [Link] library is not in the
same directory as the file that calls Sprockets, it’s nested.

Angle brackets will work.

Running Sprockets
After you’ve read the rest of the docs (see the sidebar!) and set up your
project, you can process it up with the included sprocketize command-
line tool:

$ sprocketize javascripts/.js > [Link]

This will grab all the .js files from the javascripts/ folder, concatenate
the contents according to the rules you’ve set, and the result will be
[Link].

21
Loadtime: Con-ca-te-nation

Setting Your Load Path


You can specify the load path for inclusions, etc., when you run the
sprocketize command:

$ sprocketize -I YOUR/PATH/HERE javascripts/.js > [Link]

You can use this approach to reuse your own custom JavaScript
components across multiple proejcts, without making a copy each time
you want to use them. (Yay! DRY!)

Remember that when you use //= require <[Link]> with angle
brackets, Sprockets will search for the first matching file inside your
load path… and if you use quote marks, //=require "[Link]", it will
automatically try to load that named file in the directory of the including
file.

Using Sprockets with Web Frameworks


Sprockets comes with a plugin for Ruby on Rails, and a generic CGI
script. There’s also a PHP Sprockets in development by another guy (not
from the original team).

Sprockets as a Rails Plugin


After you install the gem, you can go a little bit further and install the
Ruby on Rails plugin in your app of choice:
22
Loadtime: Con-ca-te-nation

script/plugin install git://[Link]/sstephenson/[Link]

It will do a significant amount of the work for you! Check out the docs
for details.

Sprockets for PHP


Stuart Loxton has written a PHP port of Sprockets called—get this—PHP-
Sprockets.

[Link]

It looks like he’s got a couple features planned that aren’t implemented
yet (the gzip directives, for example). Github’s a great way to follow his
progress. Maybe even nag him a bit. Or, better yet, contribute.

Sprockets for Everything Else


The Sprockets gem installation also comes packaged with a regular old
CGI script that you can use in any other environment. As the docs say,
you can find it and brief instructions along in the directory:

ext/[Link]

Not sure where that is? Hit up your command-line with the following
command to get the path:

$ gem which sprockets


23
Loadtime: Con-ca-te-nation

Other Fun Stuff in the Docs


But wait, there’s more!

Sprockets has a number of other toys in its sandbox, and it’s sharing
them aaaaall with you.

Visit the clean, well-written documentation to learn more about how


Sprockets can help you:

• clean out your code comments (selectively) from the final result
*PDoc
• generate documentation (PDoc-style*) from special structured
PDoc is a JavaScript documentation
comments system that generates really nice looking
• bundle assets, like CSS and images, for JavaScript plugins and useful HTML-based documentation
• insert string constants into the final result, e.g. versions, copyright info, from special source-code comments.
etc. [Link]

Go Read the Docs


Lovely, clear Sprockets docs (with useful examples) are here:
[Link]

24
Chapter 5
Inlining & Precaching
Like Inline Skating, but without the scabbing
Mama always told you to separate your concerns. Or was it
that there’d be days like this?

Anyway.
Common wisdom in web development circles says: separate content
(HTML) from presentation (CSS), and to keep both of those faaaaar
away from dirty old function (e.g. JavaScript code).

And just like every other kind of common wisdom, there are exceptions.

Brace yourself, because we’re about to tell you that sometimes it makes
sense to smush your CSS and JavaScript into the very same file with your
HTML.
We don’t mean squishing your CSS
Inlining Isn’t Evil into attributes inside your HTML tags
Sometimes smushing your CSS and JavaScript into one file with your (or JavaScript, either).
HTML—called inlining—is not merely okay, but actually really beneficial.
We mean inlining the whole contents
It’s natural for this to feel wrong. Just know that it’s right. of files. In the appropriate places.

Proof: Google inlines. And their whole motto is “Don’t Be Evil.” So it


Loadtime: Inlining & Precaching

can’t be evil. Ipso facto!

Why Inline
The point of inlining is three-fold:

• it reduces the network load (one file stream instead of several)


• your page will download more quickly, because the browser doesn’t
have to handle multiple file streams
• when cached, the cache’s just one file, potentially improving
performane

When to Inline
Based on these benefits, there are a couple scenarios where inlining
makes great and perfect sense:

A simple page that absolutely must have top performance all the time
Example: Google’s search page, which is, in fact, inlined out the wazoo.
Or in the wazoo. Whatever.

A page that is visited only once in a session, like a portal homepage


Example: Any special page/area that has code/CSS that is not shared
with any other part of your app (making caching the code or CSS
separate a net zero benefit), like a login or thank-you-for-signing-up page.

26
Loadtime: Inlining & Precaching

How to Inline
Inlining can be tougher than it sounds. There are three major steps. Are
you with me?

Good.

First, open your CSS and JavaScript files for the page you plan to inline.
Second, open up the HTML source for that page.
Remember that your JavaScript still
needs to be positioned in the right
Paste the CSS and JavaScript in (in their appropriate locations).
locations in your HTML file; still,
ideally, at the end.
Save.

But seriously, if your to-be-inlined area is a big complex beast and you’re
more comfortable working with the files separated, you may want to
consider a deploy hook that will compile them for you.

Pre-caching: A Beautiful, Sneaky Trick


You know what caching is.

How about pre-caching?

I hear you: Is that like reading the user’s mind and giving them the
cached files before they even ask for them?
27
Loadtime: Inlining & Precaching

In a word… yes.

What’s Pre-caching?
Pre-caching’s a sneaky little low-tech trick that can definitely make your
user’s experience seem faster.

Here’s the basic idea:

1. your customer loads up a page that they have to get through, like a
sign-in or country selection page (reserving judgment on the wisdom
of country selection pages)
2. while he’s busy fiddling with the thing he needs to do, you’re loading
JavaScript and CSS files in the background—files that aren’t even
needed on this page!
3. once your customer’s done with that page, the external stuff
necessary for the next page will already be loaded in his cache

Why Pre-cache
According to Yahoo!’s research, at any given time, 20% of users will have
a “no-cache” experience when visiting your web app.

Now, we tend to think this number might be a little skewed because of


the Yahoo! web properties, but it’s hard to tell.

If you assume those numbers are globally accurate, that means that 20%
28
Loadtime: Inlining & Precaching

of your visitors won’t have any of your carefully configured-to-cache files


cached.

It’s like they’re starting fresh. Every damn time!

So if you have to put a road block in their way anyway (like a sign-
in page), why not take that opportunity to speed up their subsequent
experience?

How to Pre-cache
The simplest way to pre-cache is to simply include the JavaScript or CSS
files at the bottom of the roadblock page, just like you would on any
other page.

Just be sure they’re not going to conflict with anything!

29
Chapter 6
Under Compressure
JavaScript Compression, Not a David Bowie cover band
This section will be fun and confusing because of the
terminology. Let’s have a word definition war!
There are essentially three ways to squish down your JavaScript code
files, divided into two camps: the types that modify your code, and the
types that do not.

MODIFYING THE CODE:


1. Packing. The most popular, and most horrid; obfuscates code and
requires eval() to unpack.
2. Minifying. Like packing, but without the horrid: instead of
obfuscation, minifying is relatively sane, involving the removal of
comments, white space and the shortening of variable names, etc.

NON-MODIFYING THE CODE:


3. Deflating. This is a fancy word for “gzipping.” This method doesn’t NOTE! Lots of tools that call
change your code in any way, just gzips it right up, to be quickly themselves minifiers actually pack,
decoded by the web browser. too. Watch out. Don’t obfuscate
your code.
The Problems with Packing
One of the most common questions the Prototype team receives is

How can I minify Prototype?


Loadtime: Under Compressure

The answer is:

For the love of god, don’t!

While packing and minifying are technically different approaches—packing


goes just a step too far—when people say “minify,” they usually mean the
kind that compresses the code (obfuscation).

Packing has caught on in the JavaScript community, but it’s an actively


destructive method of reducing JavaScript file size.

You shouldn’t “minify” your JavaScript with any of the tools that add
obfuscation, because the client has to decompress it with JavaScript. This
can be slow.

Let’s try that again: This can be very, very slow.

And it can break your code!

Some of these suckers are not 100%-capable of following the JavaScript


specification. That means they can introduce bugs. Bugs, caused by your
performance tweaks! Not a good thing, people.

As an added bonus, debugging packed files is hellish.


31
Loadtime: Under Compressure

If you really want to reduce the size of the content of your JavaScript Minifying and packing are last-
files, we strongly recommend you stick to the well-behaved white-space ditch efforts. You should try the
removal, variable renaming type. other stuff (cleaning up your code,
caching, getting a faster server,
But, except in extreme circumstances, you may as well just stick to gzipping, etc.) before trying to
gzipping. minify the heck outta your files.
That’d be premature optimization.
BIG HONKIN’ EXCEPTION: the iPhone is hardcore about
caching—for obvious reasons, it being a little handheld device and And we all know what that means.
all. To persuade iPhones to cache your stuff, you need to keep
each file under 25k. This is where minifying can come in handy.
See [Link] for more
info on this.

Good Minification
I’ll assume we’ve suitably scared you about minification vs packing, and
all the fallout that may ensue if you confuse one for the other. But just in
case… good minification does not obfuscate your code!

Now we’re set on what good minification isn’t, but how about what it is?

Definition: Good minification involves the removal of whitespace (tabs,


spaces, linebreak) and comments, plus the shortening of variable names
(inside functions only, so it’ll still play nice with other code).
32
Loadtime: Under Compressure

Good minification would take this function:

function upcase(string){
// this is a pretty useless comment as
// it is obvious what is going on
return [Link]();
}

And transform it into:

function upcase(s){return [Link]()}

How to Minify (Properly)


We’d recommend the YUI Compressor, a Java-based command line
tool from Yahoo!’s JavaScript gurus.

You can invoke it as follows:

java -jar path/to/[Link] -v [Link] -o source.


[Link]

Given our longer example above, this is the actual result that the YUI
compressor worked out:

function upcase(a){return [Link]()}; 33


Loadtime: Under Compressure

Looks familiar, huh?

Download the YUI Compressor & check out the docs at


[Link]

Hint: Use Verbose Mode


The -v option sets the Compressor to verbose mode. And in verbose
mode, it’ll chew you out if it finds other things in your code that can be
optimized for compressing.

For example, the Compressor doesn’t like multiple var statments inside
one method, & it will tell you so. It’ll also identify dangling variables that
are declared but never used, and other such good-to-know things.

If we add some useless, badly written code to our example:

function upcase(string){
var a = 1;
var b = 2;
return [Link]();
}

34
Loadtime: Under Compressure

The Compressor will not let it pass without comment:

[WARNING] Try to use a single ‘var’ statement per scope.


(string){var a=1; ---> var <--- b=2;[Link]()

[WARNING] The symbol a is declared but is apparently never used.


This code can probably be written in a more compact way.
functionupcase(string){var ---> a <--- =1;var b=2;returnstring

[WARNING] The symbol b is declared but is apparently never used.


This code can probably be written in a more compact way.
string){var a=1;var ---> b <--- =2;[Link]();

It’s just like having a smart, well-meaning, but slightly passive aggressive
friend looking over your shoulder, all the time!

But seriously, the Compressor gives good hints. It’d do you good to listen
to it.

Even if it could use an adjustment in its bedside manner.

35
Loadtime: Under Compressure

Gee! Gzipping is the Answer Want the technical dirt on the gzip
Gzipping is the best solution for JavaScript file size, bar none:
algorithm? Be careful what you ask
for: [Link]
• You can get a 1:4 reduction in size with gzip. That’s from 4K to 1K,
or 40K to 10K.
• Gzipping doesn’t remove white space, or alter your variable or
function names, making it easier to debug
• Gzipping is done by your web server on the way out, meaning you
can configure it & forget it (after testing, of course)
• Gzipping offers a low performance hit compared to script
obfuscation

Don’t forget that gzip is the same compression method used for GIFs and
PNGs (and zip files, too, of course). Web browsers are already all over
that. It’s not some crazy new hippie web 2.0 thing.

Configuring Apache 2.x


It’s easy to configure Apache 2.x to gzip stuff using the built-in
AddOutputFilterByType extension (phew!).

Combine this with proper caching settings, and users will download your
gzipped JavaScript, it’ll get unzipped by the browser, and that unzipped
source will get cached for as long as you need.

36
Loadtime: Under Compressure

Here’s our setup for freckle time tracking:


NOTE: If you’re copying & pasting
this into your config file, make
# You need these Apache2 modules, which probably come with your
sure you’re not doubling up on the
Apache installation,
ExpiresByType stuff that we covered
# but the locations might vary. This example is from Ubuntu 8.04.
LoadModule deflate_module /usr/lib/apache2/modules/mod_deflate.so earlier! (And don’t forget these code
LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so samples are all available in the ebook
LoadModule expires_module /usr/lib/apache2/modules/mod_expires.so directory, as real text files.)

AddOutputFilterByType DEFLATE text/html text/plain text/xml


application/xml application/xhtml+xml text/javascript text/
css application/x-javascript

ExpiresActive On
ExpiresByType image/gif "access plus 5 years"
ExpiresByType image/png "access plus 5 years"
ExpiresByType image/jpeg "access plus 5 years"
ExpiresByType text/javascript "access plus 5 years"
ExpiresByType application/x-javascript "access plus 5 years"
ExpiresByType text/css "access plus 5 years"

Header unset ETag


FileETag None

Header add Cache-Control "public"


37
Loadtime: Under Compressure

Google gzips So You Don’t Have To


So you don’t have access to your Apache’s config files. What to do?

Weell… If you’ve got a lot of custom JavaScript to gzip up, you shoulder
consider switching up your hosting situation.

There’s only so much magic we can work for ya, you know.

But, I hear you saying, I don’t have a lot of custom JavaScript. Just this
honkin’ big JavaScript framework! It’s not even mine!

Well, good news, then! Google to the rescue!

Google offers a bunch of the most popular JavaScript libraries, hosted up


gzipped and with good cache settings for your convenience.

Aside from not having to do the work yourself, this centralized hosting
means a potentially download-free experience for your users.

For example, Joe visits Web App A, and it uses Google’s hosted
Prototype [Link]. Then Joe visits your Web App B, which also uses
Google’s hosted Prototype [Link].

Joe’s browser only downloads the library once, the first time. Whoopee!
38
Loadtime: Under Compressure

Offered Libraries
As of time of writing, the libraries included are:

• jQuery
• jQuery UI
• Prototype
• [Link]
• MooTools
• Dojo
• SWFObjectNew!
• Yahoo! User Interface Library (YUI)New!

Nice!

Go to this URL for a full list & detailed instructions:


[Link]

39
Loadtime: Under Compressure

How it Works
To use Google’s hosted libraries, just reference the libraries like this:

<script src="[Link]
prototype/[Link]/[Link]" type="text/javascript"></script>

And so on. We don’t recommend to use the approach described by


Google (“[Link](...)”) as it adds unneccessary overhead.

But Not All is Perfect in Googleland


There are some reasons you shouldn’t use Google’s hosted code:

• It may incur an additional DNS lookup (boohoo! Not typically a big


deal).
• It’s remote. There are times when Google may not be reachable by
your user, or the Google servers with the libraries may be slow. This
sounds unlikely, but we’ve experienced this.
• You can’t have edge libraries. Google only hosts stable versions, not
experimental releases or the most current from the source repository.
• You can’t hack the source (obviously).

Generally speaking, it makes sense to use your own libraries if you can
get the server properly configured for caching, and if you are deploying
an app on a long-term basis.

40
Loadtime: Under Compressure

Translation: if you’re charging money for your app’s performance (or your
reputation’s riding on it), we recommend you suck it up and get better
hosting.

But Google’s hosted libraries are perfect for quick projects, and non-
mission-critical apps when you’ve got inexpensive hosting without access
to web server configs.

The Fruits of Compression


Don't take our word for it: Here are some examples of just how much
proper GZipping combined with non-obfuscating minification can do!

[Link] 138k
[Link] Minified 80k
[Link] Minified+GZip 24k
[Link] 120k
[Link] Minified 56k
[Link] Minified+GZip 19k
0k 25k 50k 75k 100k 125k 150k

41
Chapter 7
Cover Your Assets
an EASY way to increase Zippiness
Streams, as in data flowing and burbling over picturesque
little rocks, and concurrent, as in more than one at a time.
Quick review. Most of us tend to think (& therefore operate) as if the
browser request cycle looks like this:
k_\j\im\ii\Z\`m\j
2 gifZ\jj\jk_`ji\hl\jk

L
HTM

pfliYifnj\i
IMG
3 [fnecfX[jXccXjj\kj
pfliYifnj\i
1 i\hl\jkjXi\jfliZ\ JS

CSS

In content distribution terms, an


asset is any file or resource your
pfliYifnj\i HTML document includes, such as
4 i\e[\ijk_\gX^\
\o\Zlk\jk_\Zf[\ 5 ]`e`j_\[ CSS files, JavaScript files, images,
movies, Flash files, you name it.
Loadtime: Cover Your Assets

If only it were so! But it is not. In fact, Step #3—where your browser
downloads all assets (aka supportive files)—is much more complicated
than that.

In reality, if we zoom in on the process, it looks more like this:

HTM
L
:FE:LII<EK N8@K@E>
IMG

JS

CSS

Browsers will only open so many connections with one host (e.g. www.
[Link]) at once. We call these concurrent streams. And the
default most browsers use is criminally low: 3 concurrent streams, maybe
4.

43
Loadtime: Cover Your Assets

This low, low limit on concurrent streams is an unfortunate throwback to


the days of bleeps and bloops.

And yet in 2009, it’s a common loadtime performance issue for web
apps with lots of little files.

How it Works (or Fails to Work)


Picture this: your web app’s main screen has 20 assets (including images,
CSS files, and JavaScript files). That’s not a high number when it comes
to creating beautiful, dynamic web apps.

A customer of yours logs in and so her browser requests that main


screen. (She’s on a friend’s computer, so nothing is cached.) The
first 3 files the browser chooses to download will begin to download
concurrently, and the next 3 (or so) will have to wait til one of those 3
slots free up... and on and on, until every asset is downloaded.

It’s easy to see how this can have an unfortunate impact on the zippiness
of your customer’s experience with your web app.

How You Work Around It


The first line of defense against this particular problem is to religiously
reduce the number of files you have to download for the page to
render: squeeze ‘em together, and optimize, to get that number as you
can manage (as we’ve talked about earlier). 44
Loadtime: Cover Your Assets

The second line of defense, of course, is cheating.

Using Multiple Asset Hosts


Remember, these limits are per host. And a host is a domain name like
[Link]. To underscore: [Link] and [Link].
com are not the same host. The hostname (the first bit, www1 and www2)
is different, and so the browser will treat them as different servers.

This leaves us room to get tricky: You can work around the annoyances
of browser’s concurrent stream limit by using multiple asset hosts.
9<=FI<DLCK@GC<8JJ<K?FJKJ 8=K<IDLCK@GC<8JJ<K?FJKJ

8%[fdX`e%Zfd 9%[fdX`e%Zfd

:FE:LII<EK :FE:LII<EK
:FE:LII<EK N8@K@E>

45
Loadtime: Cover Your Assets

That means that instead of referencing your assets like this:

<img src="/images/[Link]" />


<img src="/images/[Link]" />
<img src="/images/[Link]" />

You can reference your assets, like this:

<img src="[Link] />


<img src="[Link] />
<img src="[Link] />

Your pages could download up to 3x faster (bandwidth allowing).

Requirements: 1 Server, 50cc Sweat & Blood


And the true beauty of this little trick is that you don’t even need 3
physical servers to use it. You can fake it!
Bonus: Some web
With a little bit of effort, you can configure simple DNS pointers to the
development frameworks
same old server, and then configure your server software to respond to
come with support for
those hostnames, too.
multiple asset servers out-of-
the-box (e.g., our friend Ruby
Word to the Wise: Don’t Randomize!
on Rails).
Watch out that you don’t just randomly assign hostnames.

If you reference the same asset with different hostnames, the browser
can’t tell that the assets are identical… and all your work on caching will 46
Loadtime: Cover Your Assets

go out the window.

In this case, for example, your user will have to download the image
twice, rather than pulling it from cache the second time:

<img src="[Link] />


<img src="[Link] />

If your web development framework doesn’t internally manage multiple


asset servers, then you’re going to have to ensure consistency yourself.

The best way is to build a checksum from the filename of the asset and
calculate the modulo for the number of asset server hostnames you use.
Keep it around to verify.

If that sounded like greek to you, that’s probably a good thing.

This technique is really best for small, hand-optimized landing pages,


or for bigger web apps that use a framework that has solid support for
this. Going it alone on large web applications can be an unpleasant
experience.

47
Loadtime: Cover Your Assets

For Best Results, 4’s the Magic Number


You’ll get the best results with about 4 asset servers.
Use more than that, and you may start going backwards.

There are three reasons why:

• the extra DNS lookups will defray the benefits of the faster loading
cycle
• you’ll also have to spawn more network connections than necessary
• and almost all servers are already configured to use the HTTP 1.1
keep-alive directive, which means your server will keep sending files
down the pipe as long as the connection’s open

Of course, if you follow the rest of our advice, this doesn’t matter for
your JavaScript files. You should only have one of those!

48
Chapter 8
You Need an Upgrade
C’mon, don’t you watch MTV Racks?
If you’ve already got a high-octane hosting environment
with a slew of slices and performance monitoring up to
here, you can skip this section. Carry on!
There comes a point in every app’s life when it simply outgrows its
humble shared hosting origins.

It’s not just page weight and open streams and DOM churning that can
slow your app down.

Out of Sight, Not Out of Mind


What’s happening on your back-end comes before all those things, and
could potentially also be a source of slowness. So too with the pipe
between your back-end and your customer’s front-end.

You know what I mean.

Before doing anything drastic with your JavaScript code or reworking


your app in any way, be sure that you’re getting good performance out
of your server to begin with.
Loadtime: You Need an Upgrade

If your web app views are taking a really long time to compose because
of complicated queries, slow libraries, or simply very high server load (or
very low bandwidth), there’s no amount of JavaScript tuning you can do
to offset that.

Catching Back-end Issues


Here’s how to spot a back-end issue:

• the longest wait is from the browser request to when files start
downloading
• pages get to the “mostly complete” phase but the browser keeps
waiting on
• the final bits (watch out for remote JavaScript)
• pages render fast once they’re downloaded (bandwidth problem)
• the Net tab of Firebug doesn’t indicate that download speed is
the issue (in the case of a back-end performance problem, not a
bandwidth problem)

Profiling Back-end Issues


For Rails apps, there are two great services that can help you identify
where the slowdowns occur in your app:

Rails
FiveRuns [Link]
New Relic RPM [Link]
50
Loadtime: You Need an Upgrade

And some how-to guides on other methods:

How to Profile Your Rails Application


[Link]
Profiling Rails end-to-end
[Link]
Benchmarking and Profiling Rails
[Link]

Django
Profiling Django Applications
[Link]
Quick profiling your Django web site with debug_toolbar
[Link]

PHP
Improving Performance by Profiling PHP Applications
[Link]
PHP Performance Profiling
[Link]
XDebug
[Link]

51
Chapter 9
Reduce Complexity
Tis a gift to be simple, especially for Page Rendering
A complicated DOM is an unfortunate beast. Hard to read,
hard to parse, slow to render... and it’ll slow your JavaScript
down, too.
When it comes to the DOM—that is, the structure of a document’s HTML
and the API that browsers supply for manipulating it—simpler is better.
Unlike that foregoing sentence.

Simpler means fewer DOM nodes in the DOM tree, which means faster
sites.

Simplifying your DOM can really affect the runtime performance of your
app, too, but it also affects your loadtime performance. More complex
DOMs take up more bytes, and unnecessary nodes (especially nested
divs) can add a lot of rendering time to your user’s experience.

There are two basic areas where you can optimize:

• General complexity.
• Browser-specific complexity.
Loadtime: You Need an Upgrade

General Complexity
Here’s an obvious (and simplified) example of an unnecessarily complex Reduce CSS complexity. Simplifying
set of nodes: your CSS can make your rendering
zippier, and significantly improve your
<p> animation speed. Get yourself down
<b>This is bold</b> to as few rules as possible, and avoid
</p> having many rules that overwrite
other rules (for example, through
This example has two elements (p and b) and three text nodes excessive use of !important). It’s a
(whitespace and the text in the b tag). good practice all around!

It could be simplified as:

<p class=”important”>This is bold</p>

Yep, that’s right kids—with that one stroke, you’ve axed half the elements
and 2/3rds of the text nodes. But, as we said, that is a simplified
example.

In reality, sure, you’ve got unnecessary b and em tags in your app’s


HTML structure. But what you really need to be watchful for is nested
divs and spans.

53
Loadtime: You Need an Upgrade

Watch out for Ajax


Another area to step carefully: Ajax that dynamically updates visible
nodes.
Yeah, we love Ajax, too, but browsers vary wildly in the amount of nodes
they’ll re-render when you insert or update content. That means that
if you insert into node A, other nodes around node A may also get re-
rendered. Which can be sloooow if you’ve got a lot of nodes.

This isn’t normally an issue. But it depends!

If your page structure is complex, if you do a lot of Ajax updates, and


especially if your users aren’t visiting on top-notch hardware, things can
go south very quickly.

Or slowly. Depends on how you look at it.

Browser-specific Complexity
Lots of people—purists—will tell you that sniffing browsers is a terrible
idea. But in certain situations, terrible ideas can actually become very
good ideas, indeed.

There are times when only one type of browser (coughIEcough) requires
additional complexity, when the others are chugging along just fine.

54
Loadtime: You Need an Upgrade

Case in point: rounded corners.

With a number of modern browsers, you can do rounded corners with


the border-radius CSS property and just leave it at that. For the others,
well, you’ve got to nest some divs or apply other hacks.

Why not send those hacks—and that extra complexity—just to the


browsers that need ‘em? You can keep your pretty non-stabby round
corners, and also reduce complexity for every other browser.

In modern web development environments (like view helpers in Ruby


on Rails), it’s easy to customize the actual HTML output based on your
visitor’s user agent.

It’s not such a bad idea after all.

When one really needs to skin a cat, one can only be so squeamish about
the tools one uses to do the skinning. ‘

55
Chapter 10
JavaScript, On-Demand!
Right where you want it, when you want it.
If you’ve tried everything else, on-demand JavaScript may be
an option for you.
Sometimes you only rarely have need of certain sets of code. But these
bits of code might cause your initial page loading time to creep up
towards the unacceptable zone.

Why not load JavaScript on demand in this case?

That feedback form that’s just used once every 1000 pageviews -- spare
your users the cruft and load it on demand.

Inserting JavaScript on Demand


It works by dynamically inserting a script tag into the DOM.

// create a script element


var script = [Link]('script');
[Link] = 'text/javascript';
[Link] = '[Link]

Once you’ve prepared that, you simply insert it into the DOM (in the
Loadtime: JavaScript, On-Demand!

proper place):
// insert into the dom (at the end of the body element)
[Link](script);

That’s all. Your JavaScript will be loaded. Keep in mind that there won’t
be any events called (the DOM is already loaded), so just initialize things
directly at the end of your JavaScript file.

Caveat: mind that the protocol used to load in the JavaScript file should
be the same as for the containing page, so if you’re on a secure site and
use SSL, use https:// to load in the file.

If you’re using Google Analytics, you might have seen the following line,
that you might want to ‘borrow’ and use for your own evil purposes:

var gaJsHost = (("https:" == [Link]) ?


"[Link] : "[Link]

57
INTERMEZZO
SSL & Serving JavaScript
Not Quite a Chapter, More than a Sidebar
SSL is ever-more popular among web services, so you have
to be ever-more vigilant about your JavaScript serving.
There are two brick walls you might hit with your rich app and SSL:

• mixed content problems


• caching problems

Mixed Content Warnings


In brief: you have to serve JavaScript over SSL if your visitor’s connection
is SSL.

There are exceptions… kinda. With Safari only, it’s possible to serve
assets over plain HTTP, when the main connection is SSL.

But your users aren’t all Safari users, are they? Then that exception
doesn’t help you at all. Better to not push your luck.

You’ll know you forgot to fix this problem if your users write you about
scary “mixed secure/non-secure content” warnings.
Loadtime: JavaScript, On-Demand!

JavaScript & SSL & Caching


The second problem is that browsers (especially Firefox) are loathe to
cache SSL assets.

There’s a good reason for this, of course, in theory—the connection is


supposed to be secure for your visitor’s security and privacy. If browsers
cache SSL-protected sites aggressively, then anyone who can gain access
to the computer’s file tree can undo much of the good that SSL did.

You can counter-act, this, though, with a special HTTP header:

Cache-Control: public

In addition to the proper Expire headers, this tells the browser that it’s
okay, go ahead and cache it, because it’s not a potentially sensitive file.

So be sure to ONLY add this to files that aren’t sensitive! No JSON or


Ajax responses, please!

Our sample configurations do this. Check out the Goodies folder for
more.

59
Chapter 11
Smush Those Images!
It’s like shrinky-dink, for the web—no Fireworks required
Everybody used to talk about optimizing images. Nobody
talks about it any more.
We think this is because they’re too busy thinking up ridiculous Web 2.0
app names. It’s certainly not because the tools are so much better than
they used to be.

Photoshop—the most famous image editor—does a terrible job of


exporting images for web use. (We hear Fireworks is better, but then
again, you have to use Fireworks.)

Luckily this is 2009 and there’s an automated alternative with a funny


name. Meet [Link].

[Link] squishes PNGs and JPGs losslessly, with various algorithms,


color depth tweaks, and other clever tricks. It does this using a suite of
open source tools you’ve probably never heard of.

[Link] gives very, very good results. You can easily shave 25% off
your PNGs and JPGs without even a teeny bit of quality loss.

You could try to figure out how to do it yourself with the various
command-line tools used by [Link] on the back-end, but it’s a heckuva
Loadtime: Smush Those Images!

lot more fun to just smush. And it’s fun to say. Smush smush smush!

Two Ways to Use Smush


You can install Michael Grosser’s Smusher tool and use it from the
command-line. You could also use the [Link] web service, which the
Smusher command-line tool uses for you.

Installing smusher
To install smusher, you need Ruby, RubyGems and curl. On OS X, you’ve
already got ‘em.

Then, from your command-line, run:

$ sudo gem install grosser-smusher --source [Link]

And then, to use, just call its name:

$ smusher /path/to/your/images

If you have a lot of images, this might take a while. Go get some coffee.

Using [Link]
You can Smush on the web, without installing a thing. Upload your
images on [Link]

You can also run [Link] on your app from the Tools tab in YSlow.
61
Loadtime: Smush Those Images!

Watch out for GIFs


There’s just one thing to watch out for. You can’t tweak GIFs the same
way as image types meant for photorealism; GIFs, of course, are already
compressed.

Smusher will convert GIFs to PNGs, although you’ll have to update your
asset paths.

And it doesn’t work well at all for GIF animations. Naturally.

62
Chapter 12
Favicons Are Ridiculous
The most ridiculous performance tip EVER
Somebody, somewhere, ought to fix this problem—but until
they do, you should work around it.
Did you know that browsers will check for a [Link] file on every
page load?

They just don’t learn.

So that means that if you don’t have a favicon—even a blank one—your


users will be getting at least one 404 error every single time they load
a page. The browser doesn’t show it, but that’s what’s happening. We
don’t need to tell you what that means, do we?

(Pssst: that means it’s a slowdown!)

Custom favicons are a great idea, anyway. Even if you’re too busy or
strapped to create one that reflects your logotype, put up one with a
color that matches your UI. Or even a blank one. Whatever it takes!

And then ensure it has the proper caching/expiration headers.

Making a Favicon
Favicons are 16x16 pixels, and the path must always be /[Link]
unless you specify otherwise (see below).
Loadtime: Favicons Are Ridiculous

Learn how to make a favicon in Photoshop:


[Link]

Create one for free with this web-based service:


[Link]

Referencing a Favicon
You can either leave your [Link] file in the root directory of your
web app, or you can change its name and specify a location using this
link tag:

<link rel="shortcut icon" href="images/[Link]" type="image/x-icon" />

64
Chapter 13
CSS Sprites are Magical
Tired: Image Maps. Wired: CSS Sprites.
Do you remember how, in 1996, image maps were the
hottest thing ever? Yeah, CSS sprites are like that. But in an
alternate reality.
Image maps and CSS sprites are, in a way, flip sides of the same coin.

Like image maps, a sprite image is a collection of several conceptually


distinct graphical elements, smushed into one file—and loaded just once.
Like image maps, the goal of sprites is to reduce the number of HTTP
requests/file downloads your visitor must do.

The key difference lies in the application. For image maps, the whole
image is meant to be seen by the user, and you use special HTML
coordinates to say which areas are clickable and what they do.
Google Uses Sprites
Sprites Save Silly Amounts of Time
With CSS sprites, the full image is never revealed to your visitor—it's
like a junk drawer of interface elements, and you use CSS to select the
right bit for the job, when and where you need it. You set the full image
as the background image for a DOM element, and then you use those
coordinates to select only the part of the image you want to reveal.
CSS Sprites are Magical

Creating Your Own CSS Sprites


There are three steps to sprite-ifying your own app:

1. Identify elements that can reasonably smushed into a sprite file


2. Create the sprite file
3. Write the CSS necessary to display the relevant sprites where and
when you need 'em

Which UI elements to include?


Some types of graphical interface elements really lend themselves to
spritehood, if you catch our drift. If you're looking at the following, they'd
probably make great sprites:

• icons
• buttons
• anything small with hover/depress/inactive states
• bits and pieces used to build page frames, button frames, gradient
changes, etc

Amazon Uses Sprites


• graphical text elements
• illustrations or photos which will definitely always be used together
(e.g. if your front page always shows the same 4 products, why not?)

It's a bad idea, on the other hand, to do sprites for images that are rarely
used together, lots of photos, and so on.

66
CSS Sprites are Magical

How Sprites Work: Our Example

67
CSS Sprites are Magical

Creating your sprites file


Creating your sprites file is annoying.

But if you don't endure the annoyance in this stage of the process, you'll
have greater annoyance when you get to the code-writing portion. That's
because the annoying part is making sure your separate elements are
aligned and spaced properly. If they're not, your CSS is going to be a real
pain in the butt to write.

The key is to lay everything out logically, nice and regular. This way you
can determine the various “windows” using math, rather than individually
measuring each time.

If you're having a hard time doing this in Photoshop—especially with


antialiased buttons—try using a brightly colored “spacer” object. It can be
hard to space antialiased bits by hand, even using the grid, but if you use
a spacer square, things will click into place. Then just delete them before
you export.

68
CSS Sprites are Magical

Writing your CSS Code for Our Example


The process for writing the CSS for your sprites? Easy div#nav [Link] {
as pie: position: absolute;
height: 44px;
• Calculate the “window” you need to reveal the top: 20px;
correct section margin-left: 400px;
• Set the sprite image as a background image background: url(../images/navi_buttons.png);
• Set the positioning }

• Set the width and height


div#nav [Link] {
• Set the margins
left: 0px; width: 116px;
• Happy dance background-position: 0px 0px;
}
To the right is the code for our foregoing example.
div#nav [Link] {
Further Resources background-position: 0px -44px;
Need more guidance? }

Check out Dave Shea's original div#nav [Link] {


left: 120px; width: 117px;
article on CSS sprites on A List Apart:
background-position: -116px 0px;
[Link]
}

div#nav [Link] {
background-position: -116px -44px;
}
69
Chapter 14
Stop! Don't Close That Tag!
If Google jumped off a bridge, would you do it too?
If your answer is “yes,” then you almost surely want to
consider this fairly extreme technique! Remember, the fall
won't kill you—it's the sudden stop at the bottom that does
it!
Here's a whacky trick that is way less wrong than it seems. But it does
require you to write your app with an HTML4 Doctype in mind.

Turns out that, according to the HTML4 specification, there are a


number of tags where you can leave off the closing tag. And we don't
just mean img, although that's one of them.

Continued...
Stop! Don't Close That Tag!

Omittable Closing Tags


Here are the closing tags you can omit, for regular tags (as opposed to
“empty middle” tags like img):

• </body>
• </colgroup>
• </dd>
• </dt>
• </head>
• </html>
• </li>
• </option>
• </p>
• </tbody>
• </td>
• </tfoot>
• </th>
• </thead>
• </tr>

71
Stop! Don't Close That Tag!

No-Close Void Tags


And “empty middle,” or void, tags—tags that have nothing between
them, that, in XHTML, are self-closing like <img/> and <br/>:

• </area>
• </base>
• </br>
• </col>
• </hr>
• </img>
• </input>
• </link>
• </meta>
• </param>

Entirely Omittable
And there are big, seemingly important tags that you can omit entirely:

• <html>
• <body>
• <head>

Yes, it's true, according to the spec. You can do it. And Google does, as
part of their extreme tuning package.
72
Stop! Don't Close That Tag!

The Closing Tag—Er, Word


Realistically speaking, there's nothing wrong with leaving off
</body></html> or </li> or even </p>. And of course, you should self-
close tags where possible.

Google, in fact, doesn't close </body> or </html> on their homepage and


search results.

However, there are downsides you need to keep in mind:

• you have to use the HTML4 DTD


• you still may experience rendering issues (dropping </body> and
</html> seems to cause no issues, but other tags might)
• omitting tags may increase parsing and thus rendering complexity,
which means it may increase render time and runtime behaviors, even
while it decreases download time

For a mini tech video on this topic, check out Google's page “Reducing
the file size of HTML Documents”:
[Link]

73
Chapter 15
Screw IE & Invent Custom Tags
In case the other techniques aren't radical enough for you
In a world, where violence is king... wait. Is this the wrong
script? In a world, where the W3C is king, you're willing to
do whatever it takes, to break all the rules... That's better.
Want to do some extreeeeeeeeeme DOM shrinkage, and don't give a fig
for IE (and give only half a fig for maintainability)? Well, you could just
invent your own tags.

Say what?

Yeah, you heard us right: Invent your own tags, W3C be damned!

Of course, you should consider this technique highly experimental and


possibly flammable. And it doesn't work on IE at all.

What? Inventing my own tags?!


We want to let you in on a little secret:

Browsers don't care about tag names. Existing tag names are, for
the most part, nothing more than bundles of default CSS rules and
behaviors.

You can make up your own and the browsers won't blink. (Except IE.
What a surprise!)
Screw IE & Invent Custom Tags

Take this totally normal example code:

<p class="important">
<em>lorem</em> ipsum
</p>

If you were feeling spunky, you could write:

<x>
<y>lorem</y> ipsum
</x>

and declare styles:

<style>
x { display: block; font-weight: bold }
y { font-style: italic }
</style>

You can shave quite a few bytes off this way, with the same results and
no added client-side complexity.

Of course, as we said, it won't work in IE, and it's more than a bit
extreme. But if you've got a need for hyper-optimization and few IE users,
it might work out well for you.

75
JavaScript Rocks! presents...

JavaScript
Performance
Rocks
by Thomas Fuchs & Amy Hoy

find more awesome JavaScript stuff at [Link]


End of Part II... but wait, there’s more!

In Part III “Runtime” you’ll dive


into the wonderful world of
code optimization.

Any questions or ideas?


Be sure to visit our forum at
[Link]

You might also like