diff --git a/packages/documentation-framework/hooks/useIsStuck.js b/packages/documentation-framework/hooks/useIsStuck.js index 20e3f0f47f..429eb6292c 100644 --- a/packages/documentation-framework/hooks/useIsStuck.js +++ b/packages/documentation-framework/hooks/useIsStuck.js @@ -1,33 +1,44 @@ -import { useState, useLayoutEffect, useRef } from 'react'; +import { useState, useEffect, useLayoutEffect, useRef } from 'react'; + +// The prerenderer provides a partial document shim but no createElement. +const useIsomorphicLayoutEffect = + typeof document !== 'undefined' && typeof document.createElement === 'function' + ? useLayoutEffect + : useEffect; export const useIsStuck = (stickyElementId) => { const [isStuck, setIsStuck] = useState(false); const prevIsStuck = useRef(false); - useLayoutEffect(() => { + useIsomorphicLayoutEffect(() => { const scrollElement = document.getElementById('ws-page-main'); const stickyElement = document.getElementById(stickyElementId); if (!scrollElement || !stickyElement) { - if (prevIsStuck.current !== false) { - setIsStuck(false); - prevIsStuck.current = false; - } + prevIsStuck.current = false; + setIsStuck(false); return; } const syncFromScroll = () => { - const newIsStuck = scrollElement.scrollTop >= stickyElement.offsetTop; - // Only update state if the value actually changed + // Compare rendered positions: offsetTop may be relative to a different + // ancestor than the scrolling page container. + const scrollportTop = scrollElement.getBoundingClientRect().top + scrollElement.clientTop; + const stickyTop = stickyElement.getBoundingClientRect().top; + const newIsStuck = stickyTop <= scrollportTop + 1; if (prevIsStuck.current !== newIsStuck) { - setIsStuck(newIsStuck); prevIsStuck.current = newIsStuck; + setIsStuck(newIsStuck); } }; syncFromScroll(); scrollElement.addEventListener('scroll', syncFromScroll, { passive: true }); - return () => scrollElement.removeEventListener('scroll', syncFromScroll); + window.addEventListener('resize', syncFromScroll); + return () => { + scrollElement.removeEventListener('scroll', syncFromScroll); + window.removeEventListener('resize', syncFromScroll); + }; }, [stickyElementId]); return isStuck;