/

loading images in a grid

High quality images can take a few seconds to load, even on fast internet connections, but users expect websites to be interactive in much under that. Browsers typically handle this by loading in the text and layout information first, while loading the images in the background. When an image is ready for display, it appears on the page.

While this works great for most scenarios, this random popping into view can be jarring when there are many images in view at once, such as in a grid layout. When load times are slow, the problem is only made worse.

Loading before optimization

My Implementation

The solution is to load in placeholder boxes immediately, both to signify the presence of content and to avoid a layout shift when the images load. Then, once all the images have finished loading, fade them in over the placeholders with a quick animation. This ends up being much more pleasing, even on slower connections.

Here’s the CSS

.batch-load {
   opacity: 0; /* Images start completely transparent */
   transition: 0.4s; /* When they load in, they are animated slightly */
}

.image-container {
   background-color: #ebebeb; /* Image containers are given a light gray background */
}

And the JS

// Stolen from https://stackoverflow.com/questions/11071314/
// Triggers when all the batch-loaded images have finished loading
Promise.all(Array.from(document.querySelectorAll('.batch-load')).filter(img => !img.complete).map(img => new Promise(resolve => {
     img.onload = img.onerror = resolve;
}))).then(() => {
     setImageOpacity(1)
});

// If it's taken 2.5s and the images haven't finished, just show whatever we have
setTimeout(function() {
     setImageOpacity(1)
}, 2500);

function setImageOpacity(opacity) {
     document.querySelectorAll('.batch-load').forEach(function(image) {
         image.style.opacity = opacity;
     });
}

This method involves setting the image transparent on the initial load. If the browser doesn’t support JS execution, we want to make sure that the images are visible, so this bit gets added to the site footer just in case.

<noscript>
    <style>
        img {
            opacity: 1 !important;
        }
    </style>
</noscript>