Skip to content

Instantly share code, notes, and snippets.

@ryangoree
Last active September 12, 2024 18:20
Show Gist options
  • Save ryangoree/0c612d123d0c473a55ac06ee3bcfbc48 to your computer and use it in GitHub Desktop.
Save ryangoree/0c612d123d0c473a55ac06ee3bcfbc48 to your computer and use it in GitHub Desktop.
A hook to observe the intersection of elements and return the IDs of those that are intersecting.
import { useEffect, useState } from "react";
/**
* A hook to observe the intersection of elements and return the IDs of those
* that are intersecting.
* @param ids The IDs of the elements to observe
* @param options The options to pass to the IntersectionObserver
*
* @example
* ```tsx
* const [activeId, ...otherIntersectingIds] = useIntersecting(ids, {
* threshold: 0.5,
* });
* ```
*
* @see
* https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
*/
export function useIntersecting(
ids: string[],
{
root = null,
rootMargin = "0px",
/**
* The threshold (0-1) at which to trigger the intersection observer. A
* value of 1 means the entire element must be visible, while 0 means any
* part of the element being visible will trigger the observer.
*/
threshold = 0.0,
}: IntersectionObserverInit = {},
) {
const [intersectingIds, setIntersectingIds] = useState<string[]>([]);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
const isIntersectingById = Object.fromEntries(
entries.map((entry) => [entry.target.id, entry.isIntersecting]),
);
setIntersectingIds((previousIds) => {
return ids.filter((id) => {
if (id in isIntersectingById) {
return isIntersectingById[id];
}
// No change in intersection status for this ID
return previousIds?.includes(id);
});
});
},
{ root, rootMargin, threshold },
);
for (const id of ids) {
const element = document.getElementById(id);
if (element) {
observer.observe(element);
}
}
return () => observer.disconnect();
}, [ids]);
return intersectingIds;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment