master the `rootMargin` option in intersection observer.
Tags: intersectionobserver, dom, javascript
Published: 2022-06-06 16:54:05

---
title: master the `rootMargin` option in intersection observer.
published: true
description:
tags: IntersectionObserver, DOM, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qss7uuammaq8u2hf3p67.png
---

The [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) API has been around for a few years now and has pretty good browser support.

It has some nice options which can be intimidating or simple depending on what you want to do with it.

In my case, I had a horizontal scrolling list of items in which I wanted to highlight the center most item.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qss7uuammaq8u2hf3p67.png)

This is the final product.

{% codepen https://codepen.io/hariti_abdellah/pen/WNMKqKg %}

And this is a video where I go over pretty much the same details:

{% youtube https://www.youtube.com/watch?v=DbDexGmyE_4 %}

The first instinct we might have is to use an intersection observer, which is a way for the browser to tell us when some element intersects with (or not) another.

```javascript
let centerObserver = new IntersectionObserver(
  function (entries) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add("focused");
      } else {
        entry.target.classList.remove("focused");
      }
    });
  },
  // options
  {}
);
```

Where `entries` are an array of [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) corresponding to the items we tell the observer to observe, like this:

```javascript
let items = document.querySelectorAll(".item");
items.forEach((item) => centerObserver.observe(item));
```

when the `item` is intersecting ie visible, we add a `.focused` class to it.

But the problem with this observer is that it defaults to observing the items position based with regard to the viewport instead of the container element.

To fix that we can pass the `root` element options in the second argument to the IntersectionObserver.

```javascript
// ....
// options
{
  root: document.querySelector(".container");
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2niuoehpfnywwda5fo5.png)

Pretty much there, except that all the items in view getting the `.focused` class, and this is where the `rootMargin` option comes to play.

`rootMargin` enables one to enlarge (or restrict) the span of the area of the container. In this case we want the latter.

Let's say we want restrict the intersection area of the container to the middle 40%, that would be `{rootMargin: "0 -30% 0 -30%"}`

Notice the negative margin to eat into the container instead of add to it.

```javascript
let root = document.querySelector(".container");
// the options look so far like this
{ root: root,
  rootMargin: "0 -30% 0 -30%"
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8dvx57rc3gg6x8zfod1i.png)

This still depends on the items fitting within the remaining `40%` width and not being bellow `20%`, otherwise we would have two `focused` items.

One possible fix is to use some dynamic dimensions for the `rootMargin`. What we want is to leave enough (or slightly more) space for one item to intersect.

```javascript
let itemWidth = items[0].offsetWidth;
let containerWidth = root.offsetWidth;
let marginWidth = (containerWidth - itemWidth) / 2;
//...
{ rootMargin: `0 ${marginWidth}px  0 ${marginWidth}px` }
```

This will always leave enough space for one item only.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc195t6g60p56sh8vlzl.png)
One last problem (last one I promise) is that items in beginning and in the end don't have space to scroll to the midway point, so will never get `.focused`.

A possible solution is to add a `margin-left` for the first item and a `margin-right` for the last item of `marginWidth`, then fixing the visual offset by adjusting the `container`'s `scrollLeft` by the same amount.

One of the options of `IntersectionObserver` is `threshold` which is how much of the item should be visible to be considered `isIntersecting`, it defaults to `1` ie `100%`
but it would be nice if the item got highlighted as soon as it takes more space in the area of interest than any other item, so I added `{threshold: 0.51}`.
That is once it has at least `51%` shown, it gets `focused`.

The full snippet is in the embedded Codepen.

I hope you learned something new and saved some time.

Thank you for reading, don't forget to like and follow for more.