Head over to RoutePlanGo - Road Trip Planner landing page and scroll down. As the page scrolls down, you will see screenshots of RoutePlanGo nicely showing up and grabbing the user’s attention. Wonder how you can do that in your app? Use Intersection Observer.

Prior to intersection observer, such effects were done using scroll event listeners and constantly comparing elements position with one another. This approach is ineffective and will slow down your app, as this has to be done in the Main thread.

Enter Intersection Observer - which offers a better and asycnhronous way to observe intersection of elements. As soon as the observer detects an intersection, your callback will be called. No need to constantly bugging the DOM for the position changes.

To use an Intersection,

  1. You need to create an observer first.
  2. Then assign a list of elements to be observed to this observer and passing a callback.

Example using an Intersection Observer in an React component

Let’s take a practical example, such as RoutePlanGo. In the landing page I have some screenshots of the application which are under the fold. As the user scrolls down the page, I want these screenshots to have some sort of animation and grab the user’s attention.

In my case, RoutePlanGo is built using React so the landing page is a React Component. Here’s the sample code for this setup.

class IndexPage extends React.Component {
  componentDidMount() {
    // 1. Once the page is mounted, create an observer 
    this.observer = new IntersectionObserver(this.handleObserve, {
        // when null, viewport is used to check for target's visibility
        root: null, 
        
        // when our target's 60% is visible, execute callback
        threshold: 0.6, 
    })
    
    // 2. Grab the images and assign them to be observed by above observer
    const images = document.querySelectorAll("img.to-animate");
    images.forEach(img => this.observer.observe(img));
  }

  handleObserve = entries => {
    entries.forEach(entry => {
      // if is intersecting, add the classnames to animate the image
      if (entry.intersectionRatio > 0) {
        entry.target.classList.add("animated", "slideInUp")
        
        // Optionally, you can stop observing any more changes for this image.
        this.observer.unobserve(entry.target)
      }
    })
  }

  render() {
    return <div>
    <!-- Page Contents here -->
    <img className='to-animate' src="..."/>
    <img className='to-animate' src="..."/>
    <!-- Other page content -->
    </div>
  }
}

You can see this code in action over here. That’s all needed for this simple and effective technique.