[jQuery] Quickly achieved lazy-loading

[jQuery] Quickly achieved lazy-loading

Lazy loading is one of those easy wins, that will please Google PageSpeed score. It's also a nice gesture for all your readers, that are using a poor or metered connection.

If you're using WordPress, the fastest way is to use one of 27354 plugins available in WordPress' repository. You can use a dedicated solution that does only that, or maybe turn on that option inside Autoptimize plugin (that I highly recommend).

If you're running custom CMS or you just want to learn how to do it by yourself, here is an easy and really well-performing solution.

Proper lazy loading

What actually is lazy loading?

The idea is, that when you enter a website, the images are loaded only if they're visible on your screen. If the page contains 30 posts, but you can see only 6 of them, the browser should load/download only those needed.

So it's good for your internet connection and even better for your PageSpeed score. Win-win.

Downsides? None really. You might see slight delay when loading the picture while scrolling, but you can handle it in at least few way.

How it should work?

So, to make sure that the image won't load, we should remove src from its attributes. Or even better, just rename it to data-src, because that's the attribute we're going to use.

The trigger in javascript will change data-src to src when it'll start being visible on the website, and the image will load. We have to remember as well, to do that switch for images displayed on the first screen, before the scrolling event happens.

So, let's go to the code.

HTML:

(that's my code in WordPress)

<a href="<?php the_permalink(); ?>" class="cover lazy">
    <img data-src="<?php echo $thumb[0]; ?>" alt="<?php the_title(); ?>">
</a>

You can use class lazy, rely on class cover or even just on the images that have data-src attribute. So your markup can really vary, depending on a situation.

In my "modern" approach and for the sake of easier set-up of lazy loading, I moved from background with the image there and background-size: cover set, to just <img> tags with proper alts (ACCESSIBILITY!) and object-fit: cover set in CSS.

Just as easy and probably way better for screen readers, as we're not actually spawning empty links. But I'll talk about accessibility at some point in the future, as I'm still learning.

JavaScript:

(I'm using jQuery)

<script>
var $ = jQuery;
$(window).scroll(function() {
    $('img[data-src]').each(function() {
        $trigger = $(this).parent().offset().top;

        if ($(window).scrollTop() + $(window).height() > $trigger) {
            img = $(this).attr('data-src');

            $(this).removeAttr('data-src');
            $(this).attr('src', img);
        }
    });
});

$(window).load(function() {
    $('img[data-src]').each(function() {
        $trigger = $(this).parent().offset().top;

        if ($(window).scrollTop() + $(window).height() > $trigger) {
            img = $(this).attr('data-src');
            $(this).removeAttr('data-src');
            $(this).attr('src', img);
        }
    });
});
</script>

So, there are the triggers.

.scroll() is responsible for triggering when scrolling, and .load() for triggering the first screen.

You might (or may not) wonder, why I'm still checking the trigger's value in the .load() case. I could just compare $(window).scrollTop() to 0 and be happy.

Well, only in theory.

A lot of times, you can refresh the page while being at the bottom of it. I'm dev/designer so I'm doing that a lot. But in normal lives? It can still happen. Let's say you have randomly generated content. Or there is something time-senstive. Or maybe you're just checking if someone posted a new comment under your blog post?

So, the trigger there checks where on the page you are, and triggers everything in (and above) your viewport. I could probably just limit that to the things in the viewport, not the above part, but we could almost safely assume, that all that's there, is already cached in your browser anyway. And it'll definitely not affect your PageSpeed score.

That's all you need

But you can always make more. To make it... nicer.

If you don't want to see the delay, you can adjust the trigger. Play around this line if ($(window).scrollTop() + $(window).height() > $trigger).

You could multiply $(window).height() by 1.5 to trigger it faster, so it will load half of your screen before you'll reach it with your scroll. Higher number there would load data sooner.

You can make the loading smoother by CSS.

CSS:

img {
    max-width: 100%;
    height: auto;
    transition: all 0.3s;
}

img[data-src] {
    opacity: 0;
    transform: translateY(40px);
}

img[src] {
    opacity: 1;
    transform: translateY(0);
}

I can't tell you why it's 40px, but it's my default value for that kind of translations. You can obviously change that value, as well as the timing of the transition.


Have fun! And let me know, how it improved your PageSpeed score!