Speed Up Your Site with Delayed Content

Speed remains one of the most important factors influencing the success of any website, and the first rule of performance (according to Yahoo!) is reducing the number of HTTP requests. Over the last few years we’ve seen techniques like sprites and combo CSS/JavaScript files used to reduce the number of HTTP requests. But there’s one area where large numbers of HTTP requests are still a fact of life: the small avatars attached to the comments on articles like this one.

Avatars

Many sites like 24 ways use a fantastic service called Gravatar to provide user images. As a user, you can sign up to Gravatar, give them your e-mail address, and upload an image to represent you. Sites can then include your image by generating a one way hash of your e-mail address and using that to build an image URL. For example, the markup for the comments on this page looks something like this:

<div>
	<h4><a href="http://allinthehead.com/">
		<img src="http://www.gravatar.com/avatar.php?gravatar_id=13734b0cb20708f79e730809c29c3c48&size=100" class="gravatar" alt="" height="100" width="100" />
    Drew McLellan
	</a></h4>
	<p>This is a great article!</p>
</div>

The Gravatar URL contains two parts. 100 is the size in pixels of the image we want. 13734b0cb20708f79e730809c29c3c48 is an MD5 digest of Drew’s e-mail address. Using MD5 means we can request an image for a user without sharing their e-mail address with anyone who views the source of the page.

So what’s wrong with avatars?

The problem is that a popular article can easily get hundreds of comments, and every one of them means another image has to be individually requested from Gravatar’s servers. Each request is small and the Gravatar servers are fast but, when you add them up, it can easily add seconds to the rendering time of a page. Worse, they can delay the loading of more important assets like the CSS required to render the main content of the page.

These images aren’t critical to the page, and don’t need to be loaded up front. Let’s see if we can delay loading them until everything else is done. That way we can give the impression that our site has loaded quickly even if some requests are still happening in the background.

Delaying image loading

The first problem we find is that there’s no way to prevent Internet Explorer, Chrome or Safari from loading an image without removing it from the HTML itself. Tricks like removing the images on the fly with JavaScript don’t work, as the browser has usually started requesting the images before we get a chance to stop it.

Removing the images from the HTML means that people without JavaScript enabled in their browser won’t see avatars. As Drew mentioned at the start of the month, this can affect a large number of people, and we can’t completely ignore them. But most sites already have a textual name attached to each comment and the avatars are just a visual enhancement. In most cases it’s OK if some of our users don’t see them, especially if it speeds up the experience for the other 98%.

Removing the images from the source of our page also means we’ll need to put them back at some point, so we need to keep a record of which images need to be requested. All Gravatar images have the same URL format; the only thing that changes is the e-mail hash. Storing this is a great use of HTML5 data attributes.

HTML5 data what?

Data attributes are a new feature in HTML5. The latest version of the spec says:

A custom data attribute is an attribute in no namespace whose name starts with the string “data-”, has at least one character after the hyphen, is XML-compatible, and contains no characters in the range U+0041 to U+005A (LATIN CAPITAL LETTER A to LATIN CAPITAL LETTER Z).
[…]
Custom data attributes are intended to store custom data private to the page or application, for which there are no more appropriate attributes or elements. These attributes are not intended for use by software that is independent of the site that uses the attributes.

In other words, they’re attributes of an HTML element that start with “data-” which you can use to share data with scripts running on your site. They’re great for adding small bits of metadata that don’t fit into an existing markup pattern the way microformats do.

Let’s see this in action

Take a look at the markup for comments again:

<div>
	<h4><a href="http://allinthehead.com/">
		<img src="http://www.gravatar.com/avatar.php?gravatar_id=13734b0cb20708f79e730809c29c3c48&size=100" class="gravatar" alt="" height="100" width="100" />
    Drew McLellan
	</a></h4>
	<p>This is a great article!</p>
</div>

Let’s replace the <img> element with a data-gravatar-hash attribute on the <a> element:

<div>
	<h4><a href="http://allinthehead.com/" data-gravatar-hash="13734b0cb20708f79e730809c29c3c48">
    Drew McLellan
	</a></h4>
	<p>This is a great article!</p>
</div>

Once we’ve done this, we’ll need a small bit of JavaScript to find all these attributes, and replace them with images after the page has loaded. Here’s an example using jQuery:

$(window).load(function() {
	$('a[data-gravatar-hash]').prepend(function(index){
		var hash = $(this).attr('data-gravatar-hash')
		return '<img width="100" height="100" alt="" src="http://www.gravatar.com/avatar.php?size=100&gravatar_id=' + hash + '">'
	})
})

This code waits until everything on the page is loaded, then uses jQuery.prepend to insert an image into every link containing a data-gravatar-hash attribute. It’s short and relatively simple, but in tests it reduced the rendering time of a sample page from over three seconds to well under one.

Finishing touches

We still need to consider the appearance of the page before the avatars have loaded. When our script adds extra content to the page it will cause a browser reflow, which is visually annoying. We can avoid this by using CSS to reserve some space for each image before it’s inserted into the HTML:

#comments div {
	padding-left: 110px;
	min-height: 100px;
	position: relative;
}
#comments div h4 img {
	display: block;
	position: absolute;
	top: 0;
	left: 0;
}

In a real world example, we’ll also find that the HTML for a comment is more varied as many users don’t provide a web page link. We can make small changes to our JavaScript and CSS to handle this case.

Put this all together and you get this example.

Taking this idea further

There’s no reason to limit this technique to sites using Gravatar; we can use similar code to delay loading any images that don’t need to be present immediately. For example, this year’s redesigned Flickr photo page uses a “data-defer-src” attribute to describe any image that doesn’t need to be loaded straight away, including avatars and map tiles.

You also don’t have to limit yourself to loading the extra resources once the page loads. You can get further bandwidth savings by waiting until the user takes an action before downloading extra assets. Amazon has taken this tactic to the extreme on its product pages – extra content is loaded as you scroll down the page.

So next time you’re building a page, take a few minutes to think about which elements are peripheral and could be delayed to allow more important content to appear as quickly as possible.

About the author

Paul Hammond makes things on the internet. He’s currently working on Typekit, a cloud based font subscription service for web designers. Previously he managed a group of hard working supernerds at Flickr. He was involved in early versions of Yahoo Fire Eagle and Yahoo Pipes, and has helped build infrastructure for the BBC, Yahoo Bookmarks and Delicious.


Paul has spoken at conferences on subjects ranging from the future of broadcasting to how to win at Monopoly. He lives in San Francisco with 2 kids and no dog, and keeps an irregularly updated technical weblog at paulhammond.org

Photo: Cal Henderson

More articles by Paul

Comments