Image Lazy Loading Using Browser’s Intersection Observer API — A Step by Step Guide with Examples

Partha Roy
Fasal Engineering
Published in
4 min readApr 28, 2021

A few days back I was working on a web application where I needed to use Image Lazy loading. Now You may say there are hundreds of tutorials on this subject already present on the Internet, why this one? Well the technique I’m going to share with you isn’t quite popular and I can guarantee that this particular technique provides the maximum performance.

Search for Superfast progressive image loading technique ends here.

As you can see in the title itself, we are going to talk about Browser’s Intersection Observer API. Now, this particular native API has blown my mind as I saw its performance.

Let me tell you what exactly it is.

What is Intersection Observer API?

As the definition at MDN says,

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

Simple explanation

In simple words, think of a browser as a human being, who processes the information as he/she sees it, if the information isn’t yet visible that means it won’t be processed.

Let’s take a real-life example of a monument on which there are some historical stories are written, and you are reading them from top to bottom and visualising it in your mind now until you have read something you won’t be able to visualise that and therefore sequentially your brain will process the data as you read along.

Alas, the browser isn’t exactly a human being ( for good reasons obviously ), and it works asynchronously unlike a human being. So when there’s n number of elements in a Document Object Model, the browser tries to load all the resources at once parallelly. By doing so browser ensures when someone is browsing the web application it should have everything necessary already loaded and ready to be processed by the user.

The Problem Statement

Now, one major problem with the default flow of resource processing in Browser is that, if your web application has a lot of images then the number of resources to be loaded by the browser is of enormous amount, resulting in a slow, non-smooth user experience.

As I was working on such an application I had the same problem to solve, and Intersection Observer was the savior.

Let me explain via a visual representation what was my end goal exactly,

The technical summary of the problem is,

Loading only images that are visible to the user on the first load ( above the fold content ) and as the user scrolls, the next images should start loading sequentially.

This particular technique is called lazy loading images.

How I implemented lazy loading using Intersection Observer

Placeholder Image

In order to do this first, we will have a placeholder image ( which will be very small in size and sublime enough to represent that the actual image is getting loaded ) in all the image elements from the beginning.

There will be a data attribute in all the images having the actual image source.

And from the beginning, every image element will have a class saying it isn’t yet loaded, this class will be removed as those are loaded.

<img class="lazy-loaded-image lazy" src="PATH_TO_PLACEHOLDER_IMAGE" data-src="PATH_TO_ACTUAL_IMAGE" />

Now we need to load images that are getting visible by load ( all above the fold content ) and by gradual user interaction ( scroll, click, etc. browser events triggered by user ). To do so we will attach an intersection observer to each image elements having a non-loaded class ( .lazy )and whenever the browser intersects the element i.e. the element gets visible it should do the following stuff -

  1. Replace the placeholder image src with the actual image source
  2. Load the actual image from data-src
  3. Remove the non-loaded CSS class (.lazy), resulting in the element’s exclusion from Observer API.
  4. Remove the observer attached to that element.

The above process can be achieved via the below code,

/** First we get all the non-loaded image elements **/var lazyImages = [].slice.call(document.querySelectorAll(".lazy-loaded-image.lazy"));
/** Then we set up a intersection observer watching over those images and whenever any of those becomes visible on the view then replace the placeholder image with actual one, remove the non-loaded class and then unobserve for that element **/let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
/** Now observe all the non-loaded images using the observer we have setup above **/lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});

The outcome looks like below -

Also read —

Happy coding!

Read my article on different types of aggregation queries in MongoDB —

--

--

Partha Roy
Fasal Engineering

Full-Stack Engineer 👨🏻‍💻 | ReactJs Dev ⚙️ | Tech Mentor 👨🏻‍🏫