Lazy-loading with the Intersection Observer

Raymon
13-04-2018

Loading images

Loading images with the normal <img> tag, let’s the browser wait for being ready until all of the images are loaded.

  <img src="https://loadyourimages.nl/image-nam.jpg" src="This is my awesome image">

Especially if you have a website with a lot of images, it can take maybe up till 10 seconds before the user has an interactive page.

On mobile connections that can even worse. With bad or slow connections your user sometimes will wait for tens of seconds or even minutes.

But we all know that those users want to wait that long! They will leave after a few seconds!

Lazy-loading images

We want our pages loaded as fast as it is possible. Our goal should be 0 – 5 seconds, this is the amount of time a user will be patient for the page to load.

So if we avoid the normal <img src="URL">, so our pages will load a lot faster.

If we use a data-attribute to put in the URL of the image, we can put it in the src attribute to load them when it’s in the viewport.

Most developers will use libraries for lazy-loading that are using an eventListener on the scroll event to check if an element is in the viewport. But we need something better since the eventListener on the scroll is kind of heavy on some devices!

Intersection Observer

The Intersection Observer does a pretty good job in detecting if a certain element is inside the visible part of your browser.

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. source: Mozilla Docs

With this API we can listen to an element is in the viewport if they intersecting with the viewport.

Configure the Intersection Observer

We have a few options in configuring the observer.

    const options = {
      root: document.querySelector('#container'),
      rootMargin: '0px',
      threshold: 1.0
    }

    const observer = new IntersectionObserver(callback, options);

root

In the root property define the element that will be set as the viewport. Keep in mind that if you target an element (or the body) and when its height is on auto than all the elements will be set to visible. So if you set an element, set a height that is not auto, otherwise, it won’t work like expected. If you don’t define this property it will automatically use the browser viewport.

rootMargin

If the rootMargin value is set to 0, it will not look outside the root element. If you put in 10px, it will check earlier if an element is scrolled into it’s root element.

threshold

If this value of threshold is 0, it will run the callback when 1px of the element is inside the root element. When the value is 1.0 it will trigger the callback when it’s 100% inside the root element. But if you want the callback to be called when the element is 50% inside the root element, put in the value 0.5.

Target elements

In order to use the Intersection Observer, we need an element to observe some elements!

The elements we want to observe have a class name ‘fake-image’, and we are gonna loop through the elements to start an observer for every fake-image element.

We also want to make sure, that when the element is in our root element, that the observation will be stopped. This saves some power on your computer or device.

    const io = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
            if (entry.intersectionRatio > 0) {
                io.unobserve(entry.target);
            }
        });
    }, options);

    const targetElements = document.querySelector('.fake-image');
    for(let element of targetElements) {
        io.observe(element);
    }

Demo

Let’s try this out!

I’ve created a small demo to show you that Intersection Observer works really smooth.

Start scrolling, everytime an element is 100% visible it will turn green and the text will be “Loaded!”.

Browser support

The support for the intersection observer is pretty nice. It’s implemented in Chrome (desktop & mobile), Firefox, Edge, Android browser. So it’s missing in IE11 and Safari (desktop & mobile). The webkit team is working hard on it because it’s marked as “In Development” ?, so hopefully it will soon be supported in Safari (March 2018).

To support the browsers that don’t support this cool API (yet) we can use a polyfill. We can get it via npm: intersection-observer npm install intersection-observer --save.

Let’s build lazy loaded images

Now we know how we can use the intersection observer, we’re gonna make our images load asynchronously into our browser when they are in the visible viewport.

At the beginning of this blog post, I’ve shown you how most of the lazy load functionality is build last years. So let’s replace the scroll event listener for the intersection observer.

HTML

If you remember the HTML we used in the example before then you see we only need to add a <img> tag with the data-attribute data-src.

A data-attribute is a perfect solution to put in the URL, so we can put the URL in theirs.

    <div class="fake-image">
      <h2>Fake image</h2>
        <img alt="" />
    </div>

JavaScript

For the JavaScript, we only need one function to make our image load. Call the function inside the intersection observer.

<br />    const io = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        // console.log('entry: ', entry);
        if (entry.intersectionRatio > 0.9) {
          loadImage(entry.target);
          entry.target.classList.add('active');
          // entry.target.innerHTML = '<h2>Loaded!</h2>';
          io.unobserve(entry.target);
        }
      });
    }, options);

    function loadImage(imageElement) {
      setTimeout(() => {
        console.dir(imageElement.querySelector('img'));
        imageElement.querySelector('img').src = imageElement.querySelector('img').dataset.src;  
      }, 1000);
    }

The only thing we need to do in the function is, putting the URL from the data-src attribute into the src attribute.

When the src attribute will be present, the image will load in the browser.

In the JavaScript code, I’ve put in a timeout for 1 sec to see the loading happen.

Example

Let’s check out the example. Scroll the view with images down so you can see for yourself that it works.

LEAVE A REPLY

you might also like