Use IntersectionObserver with rootMargin to change when reaching 50% viewport height

mrwweb picture mrwweb · Jul 4, 2019 · Viewed 7k times · Source

I'm completely flummoxed by the rootMargin property of intersection observer.

My goal is to add a class to an element when half it's height has crossed the vertical center of the viewport.

diagram of desired result

In my current project, nothing I do seems to impact the "root intersection rectangle" and the class is always added immediately. I've tested in latest Chrome and Firefox.

Here's the reduced test case:

Answer

Knogobert picture Knogobert · Jul 6, 2019

I am quite perplexed by IntersectionObserver myself sometimes, but referring to this post, it was a lot easier to grasp for me.

What was probably giving you trouble was checking for if it actually was intersecting or not. So I added an if-statement along with the property isIntersecting that is found on IntersectionObserver entries.

I also added a check for IntersectionObserver if it is available on the client and removed root: null from the options as it should default to the viewport anyway.

If you only use this IntersectionObserver for adding a class once, don't forget to observer.unobserve(circle) or observer.disconnect() when it isn't needed anymore to prevent memory leaks.

// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

const options = {
  rootMargin: '0px 0px -50% 0px',
  threshold: 0.5 // half of item height
}

const circle = document.getElementById('circle');

const observer = new IntersectionObserver(entries => {
  const [{ isIntersecting }] = entries
  if (isIntersecting) {
    console.log('intersected');
    circle.classList.add('intersected');
  } else {
    console.log('not-intersecting');
  }
}, options);

window.addEventListener('load', () => {
  if ('IntersectionObserver' in window) observer.observe(circle);
}, false);
.circle {
  margin: 100vh auto;
  width: 200px;
  height: 200px;
  background-color: tomato;
  border-radius: 50%;
  transition: background-color 2s ease-in-out;
}

.circle.intersected {
  background-color: mediumseagreen;
}
<div class="circle" id="circle"></div>