BrowserBugs.

The Lazy-Alt Experiment

Exploring Lazy-Loading Images for Accessibility & Indexing.
by Tim Bridges.

A while back I decided to try out Lazy Loading for a project as the extra mark-up cost was insignificant in comparison to performance gains on certain pages. The best guide for me so far has been Lazy Loading Images and Video by Jeremy Wagner. If you're unfamiliar with Lazy Loading it's worth a read, but to focus on the HTML it uses the following mark-up.

HTML
<!-- An image that eventually gets lazy loaded by JavaScript -->
<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load.jpg" alt="I'm an image!">
<!-- An image that is shown if JavaScript is turned off -->
<noscript>
<img src="image-to-lazy-load.jpg" alt="I'm an image!">
</noscript>

Hey presto! With JavaScript we change the image source with the data source to display the correct image on demand which saves loading all the images at the start. For the non-JavaScript folk they still get the full image with alt text experience but sadly without the performance gains, plus side though thanks to the clever css we hide the lazy-load JavaScript version so you only ever see one image.

Testing the Status Quo

Stoked with a new discovery and armed with bLazy by dinbror I introduced lazy loading with the NoScript fallback on the project. A few weeks after the launch dust had settled I figured it was a good time to start checking the results. Using Google Search Operators I went on the hunt for the images which were lazy loaded, and soon enough I started to get some interesting results…

Real Image with Alt Text in Google Images.
As we hoped for Google has indexed the real image with the correct alt text.
Placeholder Image with Alt Text in Google Images.
Erm, it looks like Google has got a little confused and also indexed the placeholder using the same alt text.

There were also other weird outcomes, in some cases Google had decided not to index the real image at all and instead just the placeholder with the alt text, in another case where there were 6 images using lazy load the first one also indexed the placeholder with the alt text but the other 5 were indexed correctly. One thing is for certain, it's a mixed bag of results.

What happened?

Let’s get back to basics, before using JavaScript to lazy load our images were simple and accessible, each image would have a source and an alt attribute, nothing fancy.

Basic Image
<img src="/images/brussel-sprout-in-space.jpg" alt="Astronaut finds a Brussel sprout on the moon.">

Anything which strays from this and we are in uncharted territory with both search engines and screen readers, even though it's for performance gain we're upsetting the natural state of things. Let’s forget SEO for a second, as stressed by the RNIB the alt attribute plays a vital role in accessibility too as it’s used to let screen readers know what’s in the picture, uncanny really as the alt attribute is also used by a search engine crawler for the exact same purpose. This was what niggled me initially with lazy loading, forum discussions were mixed so I thought I would bother John Mueller from Google and the RNIB on Twitter about it.

It would appear the heart of confusion comes from the alt text being applied to the placeholder image, and in addition by using the alt text on the placeholder we're essentially lying to both cralwers and screen readers in certain situations. CSS and JavaScript aside, deep down in the source code is a rouge placeholder image appearing multiple times throughout the page masquerading with different alt attribute each time. Testing with tools such as Screaming Frog SEO Spider also confirmed my suspicions each reporting the same alt text for both the placeholder and the real image.

The Lazy-Alt Theory

In the reply from John Mueller had said an interesting point, to … get that information in there as early as possible & test what it renders as. which triggered my little grey cells, at the end of the day if we're already changing the source of the image with JavaScript why not do the same for the alt attribute?

In most lazy loading examples I’ve seen there has always been three potential outcomes, either the image successfully loads or fails with JavaScript, or the visitor could have JavaScript disabled. The problem with adding the alt text in advance to the placeholder is it cannot be undone, so if for any reason it fails to load we’re left with an incorrectly labelled image which I'm pretty sure was the cause of the indexing confusion.

Using a lazy alt we can change this, still based on the great article by Jeremy Wagner we can use the lazy load success to change the alt attribute in the same way as the image source. By adding a new data-alt we can store the alt text for use on lazy load success. This would mean that the placeholder image has never had the wrong alt text which might of lead to the initial confusion.

To test the theory in the example below I’m using a lazy alt on success with bLazy. You’ll see I have changed the alt attribute of the placeholder to read “Lazy-Loading Placeholder.”, this is to easily distinguish between the placeholder and the real “Astronaut finds a Brussel sprout on the moon.” image for crawl testing and screen readers.

Lazy-Loading Placeholder.
HTML
<img class="b-lazy" alt="Lazy-Loading Placeholder." src="/images/placeholder.png" data-src="/images/brussel-sprout-in-space.jpg" data-alt="Astronaut finds a Brussel sprout on the moon.">
<noscript>
<img src="/images/brussel-sprout-in-space.jpg" alt="Astronaut finds a Brussel sprout on the moon.">
</noscript>
CSS
.no-js .b-lazy { display: none; }
JavaScript
/* Hide the Placeholder for NoScript */
(function() { document.querySelector("body").classList.remove("no-js"); })();
/* bLazy */
var bLazy = new Blazy({
  success: function(ele){ /* Lazy load was successful */
    var x = ele.getAttribute("data-alt");
    ele.setAttribute("alt", x);
    ele.removeAttribute("data-alt");
  }
});

Lazy-Alt Conclusion

Adding a lazy alt method has not changed the three potential outcomes, instead it has stopped the placeholder ever using the alt text of the true image even in the case of JavaScript failure. Tested with Screaming Frog to confirm and via Chrome Developer Tools with the results below for each outcome but please feel free to test for yourself.

JavaScript Success
<img class="b-lazy b-loaded" alt="Astronaut finds a Brussel sprout on the moon." src="/images/brussel-sprout-in-space.jpg">
JavaScript Failure
<img class="b-lazy" alt="Lazy-Loading Placeholder." src="/images/placeholder.png" data-src="/images/brussel-sprout-in-space.jpg" data-alt="Astronaut finds a Brussel sprout on the moon.">
NoScript
<img src="/images/brussel-sprout-in-space.jpg" alt="Astronaut finds a Brussel sprout on the moon.">

Lazy-Alt Update

So, finally I get the error I was looking for … that said, it looks like the lazy alt did it's job!

Real Image Success indexed with 'Astronaut finds a Brussel sprout on the moon.' alt.
As you would hope the real image with the real alt text.
The failed placeholder indexed with 'Lazy-Loading Placeholder.' alt.
Whoop! When the JavaScript failed the image was indexed with the lazy-loading alt.