Getting started with Intersection Observer

Table of contents

When developing an interface, there are several request that most often are demanded, like providing animations or effects to the elements on the screen when they enter (and also exit) the viewport. Obviously these effects should be triggered when the elements are “visible” in order to enrich the user experience, if they are not, the will lose their value.

Calculating their position on the screen

We used to proceeded selecting the node in the DOM and calculating its position in the viewport or within any container listening the scroll event, getting its coordinates, the height of the window and the scroll position at each moment. With this approach, we could guess whether or not an element was being visible and acted in consequence. For example, we could add or remove a class name. The strategy was more or less like this 👇

const target = document.querySelector('.element')

document.addEventListener('scroll', () => {
  const { top, height } = target.getBoundingClientRect()
  if (top < window.innerHeight && top + height > 0) {
    // Do some stuff...

But when following this approach, we encounter a new issue. Should we throttle the scroll event?. It is not always so clear, it depends. Perhaps you are doing this once and feels like it is not necessary, we could avoid loading more dependencies to the project such as third party libraries like lodash or adding more complexity with your own implementation.

Some events are fired many times in a short period of time, such as scroll, fired constantly when the user scrolls the page. We can optimize this situation avoiding the callback to be fired with throttle, which it is, basically, a way to stop the execution of the function for a given time.

Intersection Observer

Today, using IntersectionObserver is the safest, easiest and most optimized way to get to know where an element is in a page. In addition, we do not have to tackle with the scroll.

IntersectionObserver is a JavaScript API that let us constantly observe an element. So, we could know when it enters or exits a certain area, being the watchable reference the document or a container. Then, if the user scrolls down or up or a resize event gets fired, we will be notified whether the situation of our target has changed.

It is very useful for guessing the section a user is watching and reacting to this, loading more images, some content or adding or removing styles to perform entering or exiting animations. Useful for adding more interactivity to our sites enriching the user experience.

A tipical case

const callback = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // Do something...

const options = {}
const observer = new IntersectionObserver(callback, options)
let target = document.querySelector('.element')

First things first. We define an options object for shaping the observer (now is empty), then we instantiate an observer using IntersectionObserver, we select the target we want to observe (DOM node) and finally make the observer observe the target.

In addition to the options object (optional), the observer expects a function in which we check whatever condition and make some stuff. In the example above, we evaluate if the element is inside the observable area.

Each entry is an IntersectionObserverEntry. The basic operation is checking the value of the boolean isIntersecting but it also contains more useful information.

IntersectionObserverEntry { 
  time: 149248.54, 
  rootBounds: DOMRect, 
  boundingClientRect: DOMRect, 
  intersectionRect: DOMRect, 
  isIntersecting: true, 
  intersectionRatio: 0.04942339373970346, 
  target: article.main-page-content 

How to change when the callback is fired

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

The options object fine-tune over the observer behavior and it defines when the callback will be executed.


Sometimes, most often the first times you play around with IntersectionObserver happens that it does not work as we were expecting. In these situations, double check this tips.

  1. root should be the element we want to stablish the observable area. If we does not declare it (it will be null) and if we are working inside an iframe it will not match. We must use document.
  2. Double check the values given to rootMargin and make sure they make sense. Do not collapse top and bottom because the result will be unpredictable.
  3. If the element is higher than the observable area and the threshold is 1.0 your callback will not be launched since the element will not be entirely visible within the observable area.

Multiple references

Observing multiple references at the same time is easy. You just need to iterate the collection of elements (using forEach for example) for indicating to each of them that it will be observed by the observer. Nevertheless, is the same function for all these elements.


If you are planning to add effects when the elements enter or exit an area, just remember that what is attractive and funny for someone could be annoying for another one, so maybe you could make an incremental feature or at least deactivate them if the user has this preference settled.

@media (prefers-reduced-motion: reduce) {
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;


IntersectionObserver is the easiest and more optimal way to control when an element is within a visible area in a page or section, and no matter if you find troubles when getting started, it is worths it.

Nowadays, the browsers are working for implementing the new features of the V2, which improve the current version, mostly the well known issues regarding to isVisible, a property of the IntersectionObserverEntry which is always false, so everything points out to be an API that will have its continue in the future.


comments powered by Disqus

If you find it interesting

If you have any doubt or you want to talk about this topic, if the content or our profiles are interesting to you and you think we could something together, do not hesitate to contact us on twitter or trough the email address