# CHICIO CODING

## Dirty clean code. Creative Stuff. Stuff.

Intersection Observer can improve your web applications performance by helping you to implement lazy loading of images.

In the last few months I worked hard to improved the page speed of my website (yeah, the one you’re visiting right now ). I improved all my client side code in order to be able to reach a performance score above 90 points on Lighthouse, the official Google Chrome tool to measure performance, accessibility, progressive web apps compliance and more on your web application. One of the last thing that was contained in the report was a warning about offscreen images, like the one contained in the following screenshot:

#### Implementation

First of all let’s start by creating a function called lazyLoadImages. This function takes two parameter:

• selector, that is a string that I will use to select all the document Element objects that I wanted to observe

This function will create a new instance of the IntersectionObserver object from the Intersection Observer API. This object constructor takes two parameter:

• a callback, that is the function called when an object become visible given the current configuration
• a configuration options, that let the developer customize how the Intersection Observer calculate the intersection with the viewport

After the creation of the IntersectionObserver object I attached it to the DOM elements I want to observe by calling its observer(element) method on the document Element objects selected using querySelectorAll method with the selector received as parameter.

const lazyLoadImages = (selector, loadCompleted) => {
const intersectionObserver: IntersectionObserver = new IntersectionObserver(
(entries, observer) => onIntersection(entries, observer, loadCompleted),
{ rootMargin: '50px 0px', threshold: 0.01 }
)
document.querySelectorAll(selector).forEach(image => intersectionObserver.observe(image))
}


As you can see in the snippet above, in the intersection callback I’m calling the onIntersection function. What does it do? This function checks the IntersectionObserverEntry received from the Intersection Observer as parameter. If a target Element is inside the viewport it would have the intersectionRatio > 0. When this happen I can remove the observer and start the load of the image with the loadImage function.

const onIntersection = (entries, observer, loadCompleted) => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
observer.unobserve(entry.target)
}
})
}


The loadImage function downloads the image by setting the image src field with the data contained in the data-src field. At the end of the download I remove the lazy css class, that I used to hide the image until it has been download. Then the loadCompleted function is called, where the caller can do anything it want with the image (for example I’m doing a custom animation in order to avoid a flash out effect when the image is show).

const loadImage = (image, loadCompleted) => {
image.src = image.dataset.src
removeCssClass(image, 'lazy')
}
}


This is the final script with the complete flow.

import 'intersection-observer'
import { removeCssClass } from './css-class'

const intersectionObserver: IntersectionObserver = new IntersectionObserver(
(entries, observer) => onIntersection(entries, observer, loadCompleted),
{ rootMargin: '50px 0px', threshold: 0.01 }
)
document.querySelectorAll(selector).forEach(image => intersectionObserver.observe(image))
}

const onIntersection = (entries, observer, loadCompleted) => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
observer.unobserve(entry.target)
}
})
}

image.src = image.dataset.src
removeCssClass(image, 'lazy')
}
}


There’s still one thing that I didn’t discuss yet. How can we support this type of lazy loading for the browser that doesn’t still have implemented the IntersectionObserver API? The answer is the Intersection Observer Polyfill. I installed it as a dependency of my project.
npm install --save intersection-observer