Lazy load your post thumbnails

Modern standards and good practice can be easily applied to your website by lazy loading the images contained thereon. The theory is to only load the images when you want to see them (i.e. when the part of the page where the image is located scrolls into the viewport) which reduces the inital server response size and speeds up your page load time. If your website loads slowly I’m afraid this won’t be a silver bullet to solve your difficulties, however it is a vital step in the optimization process.

To begin with we hijack the post thumbnail rendering via the post_thumbnail_html filter, and switch the ‘src’ to our placeholder image, putting the actual image url in the ‘data-imgSrc’ attribute so our script can grab it, and adding the ‘defer-this-image’ class.

/**
 * Defer post thumbnail images
 */
add_filter('post_thumbnail_html', function($html, $post_id, $img_id, $size, $attr){
    $image = wp_get_attachment_image_src($img_id, $size);

    /*
    **      Assign your placeholder url to $src
    **      Remember to Compress https://compressor.io/compress 
    **      for the smallest file size..
    */
    $src = 'http://placekitten.com/'. $image[1] .'/'. $image[2];
    
    $img = [
        'src'           => $src,
        'class'         => 'size-'. $size .' wp-post-image wp-image-' . $img_id . ' defer-this-image',
        'alt'           => get_the_title($post_id),
        'data-imgSrc'   => $image[0],
        'width'         => $image[1],
        'height'        => $image[2]
    ];

    $html = '<img ';
    foreach($img as $k => $v) {
        $html.= $k .'="'. esc_attr($v) .'" ';
    }
    $html.= '/>';

    return $html;
}, 5, 250);

Okdokes now we’ve got a function that looks up all the instances of ‘defer-this-images’, then checks over whether the image is coming into view and if so switches the ‘src’ to the intended image and removes the class.

/*
** Load Deferred Images
*/
const loadDeferredImages = function(){
    try {
        let  d = {
           top: Math.round(window.scrollY),
           limit: screen.height * 1.125,
        }

        document.querySelectorAll('.defer-this-image').forEach( function(image) {
            if(image.dataset.imgsrc) {
                d.pos = image.getBoundingClientRect()
                d.brk = d.top + d.limit
                if(d.brk >= d.pos.top) {
                    image.setAttribute('src', image.dataset.imgsrc )
                    image.removeAttribute('data-imgsrc')
                    image.classList.remove('defer-this-image')
                }
            }
        })
    }

    catch(e) {
        return false
    }
}

let winScroll = 0;
const scrollingFunc = function(){
    let move = Math.round(window.scrollY)
    if(move > 32) {
        if(move > winScroll && document.querySelectorAll('.defer-this-image').length > 0) { 
            loadDeferredImages()
        }
    }
    winScroll = move
}

window.onload = loadDeferredImages
window.onresize = loadDeferredImages
window.onscroll = scrollingFunc

And the final piece of the puzzle is to fire the loadDeferredImages function when the user scrolls in order to replace the placeholders just before they appear in the viewport..

This snippet it only targets the Featured Images, you may still find you have plenty of images within your posts and pages which this doesn’t affect at all. For that we would need a slightly different approach which i’ll endeavour to cover in my next posting.

Leave a Reply

Your email address will not be published. Required fields are marked *