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.


Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
18/12/2010
The newest version of jQuery natively supports the data attribute, you van simply use
$(el).data(“gravatar-hash”)
Just a quick addition..
Vote Helpful or Unhelpful
18/12/2010
I thought this was going to be about speeding up websites in general. I was quite surprised to see it focus on gravatars.
That said, I think it’s a good idea and I’ll try implementing this in future projects!
Vote Helpful or Unhelpful
18/12/2010
I felt so guilty doing this when I was making my portfolio. I thought there had to be a better way. What a relief!
If you don’t specify a src, what about non-JS users? What about semantics? (ooooh)
Vote Helpful or Unhelpful
18/12/2010
It’s worth noting that on Flickr we’ve disabled this type of image deferral for IE6, 7 and 8.
We found that the cost of finding all of the nodes that need images inserted, and the impact on the page that downloading all of those images had, vastly outweighed the benefits we got by making those images load after the rest of the page has finished. It made the pages very sluggish and jerky, and significantly delayed event handlers from being invoked.
More modern browsers don’t have this problem at all, and are able to run the JS and download the images without any appreciable CPU load. We are still using this technique, with data-defer-src, for them. IE, though, gets img tags with normal src attributes instead.
Vote Helpful or Unhelpful
22/12/2010
Sorry, but I have to ask again, because I’m interested in your opinion. Is there any reason, not to use something like this and wouldn’t it be a better solution as noscript users will still be able to see the images?
<noscript data-hash=“124a03bd93eacc64f799527d8aea6233”> <img src=“http://www.gravatar.com/avatar.php?gravatar_id=124a03bd93eacc64f799527d8aea6233” />
</noscript>
$(“noscript[data-hash]”).before(function(){ return “<img src=http://www.gravatar.com/avatar.php?gravatar_id=”+
$(this).data(“hash”)+”/>”;
})
Vote Helpful or Unhelpful
18/12/2010
thanks for this article. I also made a plugin for joomla that lazy load images under the fold, available on my website. That could save a lot of bandwith and speed up the loading of the webpage, specially galleries.
Vote Helpful or Unhelpful
18/12/2010
Why don’t you just use the <noscript> Tag in order to show it to the 2%?
The other 98% of the user will profit from your enhancement and in a gzipped html this will not really effect your speed.
Vote Helpful or Unhelpful
18/12/2010
Very succinct.
I do something very similar, except I use the custom attribute on an image element to hold the url. The src attribute contains a placeholder. When ready, all my script does is switch out the src with the desired image url. This is a bit faster than creating and inserting elements into the DOM.
I also determinate the readiness on scroll position so I’m only manipulating elements that are in view instead of everything. The page becomes unresponsive very quickly when you have a lot of elements to work over.
Still, your technique is a nice and simple example to get one started.
Vote Helpful or Unhelpful
18/12/2010
I haven’t tried this yet, but wouldn’t this mess with the flow of the page? i.e., would the browser not have to re-render as the images are added?
In any case, here’s a suggestion. Maybe using a default image for all users (which only needs to load once) and then switching for each Gravatar once the page is loaded would also work well.
Anyway, great technique as usual. Thanks!
Vote Helpful or Unhelpful
18/12/2010
Great article, I am ashamed to say I wasn’t familiar with the data attribute so in the past I have put that type of data in the id or class element.
Rather than not rendering an image at all wouldn’t it be better to load a default avatar and just replace the image with the correct once everything has loaded?
The browser would only see one request as its the same file and the user wouldn’t see a drastic change to the presentation when the avatars load.
Vote Helpful or Unhelpful
18/12/2010
As of jQuery 1.4.3, you can just use `$(this).data(‘gravatar-hash’)` instead of `$(this).attr(‘data-gravatar-hash’)`. Also, why use `$(window).load(function() { … });` instead of `$(function() { … });`?
Vote Helpful or Unhelpful
18/12/2010
Great article indeed, I thought of a method like this but never went to actually create it.
Concerning the non-JS users: I don’t think this method is really a handicap for them – turning off JS is.
Vote Helpful or Unhelpful
18/12/2010
Thanks everyone for the comments, especially Ross for the detailed notes on Flickr’s implementation. It’s great to hear real world experience and alternative ideas around the best way to use techniques like this.
Thank you also Mathias and Julian for the jQuery tip. I’m sure I’m not the only one who misses out on some of the features that land in every new version of jQuery.
Scott: When I get a chance I’ll look back at my notes and see what I can find about Firefox. I stopped investigating removing the image with JavaScript when I thought there were no simple workarounds for the other browsers…
Vote Helpful or Unhelpful
18/12/2010
Nice article. :) Seems like a smart approach to use for delaying non-essential images.
I’m curious: do you know of a workaround for Firefox? I’ve worked out a way to prevent images from loading in most other browsers, so this would be most helpful to hear.
Cheers!
Vote Helpful or Unhelpful
18/12/2010
Excellent article, I’ve known about deferred loading as a technique for improving site speed for a while (Github do it well), but never thought to apply to images like this.
More web designers should care about the speed of their websites, it has a real impact on a visitor’s experience, Amazon know this all too well with their studies. I’d suggest anyone who hasn’t read the Y!Slow guidelines really should, they are straight forward and can provide a great benefit.
Vote Helpful or Unhelpful
20/12/2010
Very good remark and solution, Paul. I’ve tried it on a blog of me and it made a huge difference.
I’m too interested in a workaround for Firefox because many people use that browser nowadays. Hope you post this in the comments.
Vote Helpful or Unhelpful
02/01/2011
Given how wide spread the use of gravatars are, I wonder if it’d be worth suggesting to Automattic about a feature enhancement that would allow for generating gravatar sprites?
That way, you could make a single request to the gravatar servers to generate the gravatars for a given page in one go and then just use the simple CSS sprite technique to switch them out for each comment.
Vote Helpful or Unhelpful
Impress us