UNPKG

@ehubbell/scrollspy

Version:

A lightweight scrollspy library for React projects.

87 lines (86 loc) 3.2 kB
"use strict"; const jsxRuntime = require("react/jsx-runtime"); const react = require("react"); const ScrollSpy = ({ navRef, activeClass = "active", dataAttribute = "scrollspy", offsetTop = 0, offsetBottom = 0, throttle = 100, updateHistory = true, onUpdate, debug = false, children }) => { const containerRef = react.useRef(null); const [navItems, setNavItems] = react.useState(null); const activeElement = react.useRef(""); react.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]); react.useEffect(() => { updateScrollSpy(); }, [navItems]); react.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__ */ jsxRuntime.jsx("div", { ref: containerRef, children }); }; module.exports = ScrollSpy; //# sourceMappingURL=index.cjs.js.map