Lazy Loading Gravatars in WordPress

Most WordPress themes show user Gravatars in the comment threads. It's a way of showing an image with the user, as associated by the email address used. It's a nice touch, and almost an expected design pattern these days.

Every one of those gravatars is an individual HTTP request though, like any other image. A comment thread with 50 comments means 50 HTTP requests, and they aren't always particularly tiny files. Yeesh.

Let's lazy load them.

The Concept

Lazy loading is the idea that you don't even request the image at all (no HTTP request) unless the image is visible. Meaning that, through JavaScript, we've determined the image is visible.

Lazy loading means not loading those two images that are outside the browser window, until they become inside the browser window.

In order to stop those HTTP requests for not-yet-seen images, we need to get our hands directly on the markup. If there is an <img src=""> in the HTML, there is essentially no way to stop the browser from downloading that image as soon as it possibly can, seen or unseen. So, we need to remove that src, and put it back when we're ready.

Woah, There

It's worth a pause here because we've entered some murky territory.

By removing the src of these images, and only ever putting it back with JavaScript, we've decided that we're willing to ship slightly invalid HTML and rely 100% on a script downloading and executing for these images to ever be seen.

I'm OK with that. Mostly because gravatars are just an enhancement anyway. It ain't no big deal if they never show up. I'm not a hardliner most JavaScript debates, but this seems like a particularly clear case where we can lean on JavaScript without worry.

Altering the HTML

This is the change we'd be making:

<!-- Normal image. No beating the browser preloader. -->
<img src="https://gravatar.whatever..." alt="" />

<!-- Let's change to this, which won't download anything. -->
<img data-src="https://gravatar.whatever..." alt="" />

Although a missing src on the <img> is technically invalid HTML. It almost certainly doesn't really matter in that it won't affect how anything works. If the invalid HTML bugs, you could always toss a super minimal blank GIF data URL in there, like:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" ... />

Using width and height attributes is probably a good idea too, to maintain layout and avoid reflow if and when the images to load.

Altering the HTML... in WordPress

But how do you change the HTML that WordPress spits out as part of a comment thread? Comments are slightly unusual in WordPress in that WordPress core gives you the HTML, it isn't part of your theme like most of the other HTML is.

Likely, in your `comments.php` file, you'll see this function:

<?php wp_list_comments(); ?>

Which spits out a pile of <li>'s with your entire comment thread. Not a lot of opportunity there to be fiddling with the output of images. Except, we can! We can list a callback function in there:

<?php wp_list_comments('callback=csstricks_comment'); ?>

That callback is the name of a function we can toss in our `functions.php` file. Here's an example of that function, which must return a <li>:

function csstricks_comment($comment, $args, $depth) {

  $GLOBALS['comment'] = $comment; ?>

  <li <?php comment_class(); ?>">

     <img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="50" height="50" class="lazyload-gravatar" alt="User Avatar" data-src="<?php echo get_avatar_url(get_comment_author_email($comment_ID), array("size" => 160)); ?>">

     <?php comment_text(); ?>

  <?php # phantom </li> ?>

<?php }

That's very simplified, but you can see what we've done. We replaced the src with the blank GIF, we've added a class name we'll ultimately use in JavaScript to do the lazy loading, we've added a data-src to the actual gravatar, and we're using width and height attributes for placeholding. Here's my actual complete callback live right now on CSS-Tricks.

If we shipped this right now, sans any JavaScript work, we'd still have a perfectly functional comment thread, just with images that never load.

Now We're Ready to Lazyload

The hard part is over. We're perfectly set up to do lazyloading now. If we were to write a script, it would be like:

  1. Figure out the visible area of the browser window
  2. Figure out the position on the page of every image with class .lazyload-gravatar
  3. If any of those images are in the visible area, flop out the src with the value from data-src
  4. If the visible area of the browser window changes in any way, re-evaluate the above

We could set about writing that ourselves. And we could do it! But, and I'm sure you're not surprised here, it's a bit tricky and nuanced. Cross-browser concerns, performance concerns, does-it-work-on-mobile concerns, to name a few. This is the kind of thing I'm happy to lean on other's work for, rather than roll myself.

Again, no surprise, there are loads of options to pick from. In my case, I'm happily using jQuery on CSS-Tricks, and I picked a jQuery-based on that looked pretty good to me:

The API is as simple as can be. After bundled up the lib with the rest of the libs I'm using, I just call:

$('.lazyload-gravatar').Lazy();

Look how nicely it works!

That's an awful lot of saved HTTP requests and awful good for performance.

Makes you wish web standards and browsers would get together on this and make it a native feature.