UNPKG

@ehubbell/scrollspy

Version:

A lightweight scrollspy library for React projects.

88 lines (87 loc) 3.16 kB
import { jsx } from "react/jsx-runtime"; import { useRef, useState, useEffect } from "react"; const ScrollSpy = ({ navRef, activeClass = "active", dataAttribute = "scrollspy", offsetTop = 0, offsetBottom = 0, throttle = 100, updateHistory = true, onUpdate, debug = false, children }) => { const containerRef = useRef(null); const [navItems, setNavItems] = useState(null); const activeElement = useRef(""); useEffect(() => { var _a; if (navRef) { const items = (_a = navRef.current) == null ? void 0 : _a.querySelectorAll(`[data-${dataAttribute}]`); setNavItems(items); } else { const items = document.querySelectorAll(`[data-${dataAttribute}]`); setNavItems(items); } }, [navRef]); useEffect(() => { updateScrollSpy(); }, [navItems]); useEffect(() => { window.addEventListener("scroll", scrollEvent); return () => window.removeEventListener("scroll", scrollEvent); }, [containerRef, navItems]); function scrollEvent() { setTimeout(updateScrollSpy, throttle); } function checkVisibility(element) { const id = element.id; if (!id) return null; const rectInView = element.getBoundingClientRect(); const height = (containerRef == null ? void 0 : containerRef.current) ? containerRef == null ? void 0 : containerRef.current.offsetHeight : window.innerHeight; const container = height; const elementTop = rectInView.bottom; const elementBottom = rectInView.top + height; const computedTop = elementTop + offsetTop; const computedBottom = elementBottom + offsetBottom; if (debug) console.log("checkVisibility: ", { id, container, elementTop, elementBottom, computedTop, computedBottom }); return container < computedBottom && container > computedTop; } function updateScrollSpy() { const container = containerRef.current; if (!(container && navItems)) return; for (let i = 0; i < container.children.length; i++) { const scrollElement = container.children.item(i); const scrollElementVisible = checkVisibility(scrollElement); if (scrollElementVisible) { const scrollElementId = scrollElement.id; if (activeElement.current === scrollElementId) return; const computedElementId = scrollElementId || activeElement.current; navItems.forEach((element) => { const attrId = element.getAttribute(`data-${dataAttribute}`); if (attrId !== computedElementId && element.classList.contains(activeClass)) element.classList.remove(activeClass); if (attrId === computedElementId && !element.classList.contains(activeClass)) { element.classList.add(activeClass); if (onUpdate) onUpdate(computedElementId); activeElement.current = computedElementId; if (updateHistory) window.history.replaceState({}, "", `#${scrollElementId}`); } }); break; } } } return /* @__PURE__ */ jsx("div", { ref: containerRef, children }); }; export { ScrollSpy as default }; //# sourceMappingURL=index.es.js.map