Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

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

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

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

const observerCallback = function(entries, observer) {
  console.log('intersected');
  circle.classList.add('intersected');
}

window.addEventListener('load', function(event) {

  const observer = new IntersectionObserver(observerCallback, options);

  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>
like image 953
mrwweb Avatar asked Jul 03 '19 22:07

mrwweb


People also ask

What is rootMargin intersection observer?

The IntersectionObserver interface's read-only rootMargin property is a string with syntax similar to that of the CSS margin property. Each side of the rectangle represented by rootMargin is added to the corresponding side in the root element's bounding box before the intersection test is performed.

What is threshold in IntersectionObserver?

Thresholds. Rather than reporting every infinitesimal change in how much a target element is visible, the Intersection Observer API uses thresholds. When you create an observer, you can provide one or more numeric values representing percentages of the target element which are visible.

What is IntersectionObserver?

The IntersectionObserver interface of the Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport. The ancestor element or viewport is referred to as the root.

How do you use intersection observer in react?

To use the <InView> component, you pass it a function. It will be called whenever the state changes, with the new value of inView . In addition to the inView prop, children also receive a ref that should be set on the containing DOM element. This is the element that the Intersection Observer will monitor.


1 Answers

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>
like image 156
Knogobert Avatar answered Sep 27 '22 19:09

Knogobert