What is Intersection Observer & How to implement it

What is it

Intersection Observer is a browser provided API/method to observe the visibility and position of a DOM element relative to the containing viewport or root element. 

In simple words it keeps noting if the position of your selected DOM element is in the viewport while scrolling, when your selected DOM element touches the bottom of page while scrolling you can make a method get triggered (i.e., callback)

Intersection Observer API works in an asynchronous non-blocking way, which means it will run independent of the main thread in which we are running our heavy task.

Where to use it, benefits​

– It gives a smooth scrolling experience to the user

– Lazy-loading of images or other elements as the page is scrolled

– Very efficient in case your contents’ size are large and you want them to 

  downloaded one by one

– Very efficient in case of shopping website where users keep getting new products as they scroll down

– If your page content is very long and you don’t want user to have to change       the page

–  It gives the user and infinite-loading experience

 

How it looks like

Notice as we are scrolling down, new items are being appended at bottom (which you get from API call) also notice the smooth user experience.

Intersection Observer API

Source Code

To understand the topic better we have already kept our Intersection Observer project at our Github location.

How the Intersection Observer works

We create the object of IntersectionObserver and pass the callback method to be called when the target element is intersected. Using –
 new IntersectionObserver(callback , optionalParameter)
We can pass an optionalParameter in which we tell about from how which distace from the viewport our callback method should be triggered.
 
var optionalParameter = {
    root: null,
    rootMargin: '400px',  // higher it is more eagerly the 
                          // new DOM element gets appended
    threshold: 1.0
}

DEFINATION:



Root

Refers to the element that the target is intersecting against. It can be null which would mean that the root is now the viewport.



rootMargin

It provides the margin which can be positive or negative which gets added to viewport height.

For ex – If rootMargin is 200px, the callback will be called even from a distance of 200px, in other words, it means, even when there is html element with height 200px yet to be scrolled down the callback will be triggered. 



Threshold

It can be between 0 & 1. 0 means as soon as you scroll the 0.001% of the target element the callback will be hit. 1 means you have to scroll the target element completely to get the callback. Default is 50%

AN EXAMPLE OF INTERSECTION OBSERVER
 let options = {
    root: document.getElementById('#id'),
    rootMargin: '400px',
    threshold: 1.0			
  }

var callback = entries => {
    // your logic
    // load more items with a class name 'toObserver'
}

const observer = new IntersectionObserver(callback, options)

observer.observe(document.querySelector('.className'))

After creating the object of IntersectionObserver we call the observe method and pass html elements which all we want to observe, for example elements with class name – ‘toObserver’

Let's lazy load images in UI using Intersection Observer

As show in above video, we are loading images in our website in a lazy fashion means we will not load all the images at once, as the user will scroll down, we will be noticing the scroll movement, as soon as the scroll will reach near bottom, we will trigger our callback and via callback we’ll load more images.

Notice two things as shown in above video:

1- As soon as we a card gets into visible area of screen, we add a class called “show” to the card

2 – As soon as last card is scrolled from the html, we are adding new cards at the bottom, notice the new div elements will class name cardnew(with red border) being added in UI/DOM

Things which we can control here are – 

Margin – Currently we are adding new cards as soon as our scroll position reaches near 200px from the bottom, we can increase or decrease this via rootMargin in the option.

Threshold – Means how much % of a card is to be scrolled to get the callback

 

Project structure

Index.html

It’s the only UI file, so we can directly run it in browser.

 
<html>
 
<head>
 
    <link rel=”stylesheet” href=”style.css” />
 
    <script src=”script.js” defer></script>
 
</head>
 
<body>
 
    <div class=”card-container”>
 
        <div class=”show card_at_end”>This is the first card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card”>This is a card</div>
 
        <div class=”card_at_end”>This is the second last card</div>
 
        <div class=”card”>This is the last card</div>
 
    </div>
 
</body>
 
</html>
 

Script.js

 

Notice there are two IntersectionObserver being created.

1st is adding show class to the card, we are observing all the elements with class name card

On intersection we are adding the class show, then we unobserve the card using observer.unobserve() method.

2nd is adding appending new card at bottom using loadMoreCards() method, these new cards have cardnew class (red border). In this IntersectionObserver we are also passing the options object to control Margin/Threshold

const cards = document.querySelectorAll('.card')
// First IntersectionObserver
const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
        entry.target.classList.toggle('show', entry.isIntersecting)

        if(entry.isIntersecting){
            observer.unobserve(entry.target)
        }
    })
})

cards.forEach(card => {
    observer.observe(card)
})
// Second IntersectionObserver

let options = {
    root: null,
    rootMargin: '400px',
    threshold: 1.0
  }

const lastCardObserver = new IntersectionObserver(entries => {
    const lastCard = entries[0];
    if(!lastCard.isIntersecting){return}
    loadMoreCards();
    lastCardObserver.unobserve(lastCard.target)
    lastCardObserver.observe(document.querySelector('.card:last-child'))
}, options)

lastCardObserver.observe(document.querySelector('.card:last-child'))


const cardcontainer = document.querySelector('.card-container');

const loadMoreCards = () => {
    for(i = 0; i < 3; i++){
        const card = document.createElement('div')
        card.textContent = 'card new ';
        card.classList.add('card')
        card.classList.add('cardnew')
        observer.observe(card)
        cardcontainer.append(card)
    }
}

Style.css

For better look n feel also add a jpg file with name a.jpg in the same folder.

.card-container{
display: flex;
flex-direction: column;
gap: 1em;
align-items: flex-start;
}

.card_at_end{
background: white;
border: 1px solid black;
color: black;
transform: translateX(50px);
transition: 150ms;
padding: 21px 0;
height:79px;
}
.cardnew{
background: red !important;
opacity: 0;
border: 1px solid blue;
transform: translateX(50px);
transition: 150ms;
padding: 5px;
content:url('./a.jpg');
height:79px;
}

.card{
background: white;
opacity: 0;
border: 1px solid black;
color: black;
transform: translateX(50px);
transition: 150ms;
padding: 5px;
content:url('./a.jpg');
height:79px;
}

.card.show{
transform: translateX(0);
opacity: 1 !important;
}

Summary

We just saw how easily we can implement lazy-loading using IntersectionObserver. You can get similar effect as when you scroll down a video on YouTube you get to see comments.

More from Node/Javascript:

Natalie Harris
With references from Alan Plang
132
Kyle Aniston
With references from Alan Plang
75
Kyle Aniston
With references from StackOverflow
381
Mike Morgan
With references from StackOverflow
105
Mike Morgan
Forwared by QwertyBot
560
Mike Morgan
Forwared by QwertyBot
1.4k
Kyle Aniston
Forwared by Alan Plang
1.0k

Leave a Reply

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