Blog (my web musings)

Find out what's interesting me, get tips, advice, and code gems that don't fit elsewhere.

Search Blog

I've been working on a project that is image intensive. When I say "image intensive" I mean "image intensive"; More than 60 of the blighters intent on dragging my page load times down to the depths of hell. Even with proper image compression, my page weighed over 1MB and took almost 4 seconds to load. Here are the takeaway figures for that page from GTmetrix;

3.9 seconds load time, 1.01MB size, 62 assets downloaded. Yuk!

The page in question is rather long and employs my Add Class to Target-Element when Trigger-Element is Scrolled into View elementFromTop() JavaScript function to animate the aforementioned images as they enter the viewport. I thought I'd kill two birds with one stone and combine it with Patrick Sexton's defer image script to make simple lazy-loading images.

What is Lazy-Loading?

The idea behind lazy-loading is that you only need to download the images that are visible when the page first loads. All the ones lower down can be downloaded when the user decides to scroll to them. And if a user never scrolls, they won't have a shed-load of clutter imposed upon them in the background. This saves on their wait-times (and, if they're on mobile, potential data transfer limits), and it makes your web pages load much, much quicker;

Wow! Fully loaded in less than half a second, weighing just shy of 23kb, and only 2 assets downloaded!

Of course, this lazy-loading lark is just a way of tricking the browser into thinking the images aren't there... until the user scrolls down to them.


The markup looks a little different for the deferred and lazy-loaded images; Their src points to a base64 data URI of a transparent gif, while their real image path resides inside a data-src attribute. Their markup looks like this;

<img class="img-defer" data-src="real-image.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt="" />


The deferred images are identified and triggered by the "img-defer" class, which is passed to the elementFromTop() JavaScript function via a scroll Event Listener;

window.addEventListener('scroll', function() {
    elementFromTop(document.querySelectorAll('.img-defer'), 'img-defer', 'img-load', 150, 'percent'); // half a screen below the fold
    }, false);

When the elementFromTop() scroll distance is met (half-a-screen below the fold in the above sample), the "img-defer" class is replaced with the "img-load" class.

The imgDefer() function then uses the "img-load" class to load in the real image by switching the img element's data-src attribute to src;

function imgDefer() { // '.img-load' class added via js elementFromTop() function
    var imgDefer = document.querySelectorAll('.img-load'), i;
    for (i = 0; i < imgDefer.length; i++) {
        if(imgDefer[i].getAttribute('data-src')) {
            imgDefer[i].setAttribute('src', imgDefer[i].getAttribute('data-src'));

Check out the source of the Defer Image Loading until Scrolled-To (Lazy-Loading) demo page to see everything set-up together;