use-sticky-utils.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. import { useState, useEffect } from 'react';
  2. // Custom hook that accepts a selector string as an argument
  3. // and returns a boolean indicating whether the selected element is currently sticky.
  4. export const useSticky = (selector: string): boolean => {
  5. const [isSticky, setIsSticky] = useState<boolean>(false);
  6. useEffect(() => {
  7. // Get element to observe
  8. const stickyElement = document.querySelector(selector);
  9. // Updates the sticky status based on the current position of the observed element.
  10. const observe = () => {
  11. // If the observed element is empty or not an instance of Element, return early.
  12. if (stickyElement == null || stickyElement === undefined || !(stickyElement instanceof Element)) return;
  13. // Calculate the element's offset from the top of the viewport and the value of its "top" CSS property.
  14. const elemOffset = stickyElement.getBoundingClientRect().top;
  15. const stickyOffset = parseInt(window.getComputedStyle(stickyElement).top);
  16. // Update the sticky status based on whether the element's offset is less than or equal to the sticky offset.
  17. setIsSticky(elemOffset <= stickyOffset);
  18. };
  19. // Call the observe function immediately and add it as a listener for scroll and resize events.
  20. observe();
  21. document.addEventListener('scroll', observe);
  22. window.addEventListener('resize', observe);
  23. // Remove the scroll and resize event listeners when the component unmounts or the selector value changes.
  24. return () => {
  25. document.removeEventListener('scroll', observe);
  26. window.removeEventListener('resize', observe);
  27. };
  28. }, [selector]);
  29. // Return the current sticky status.
  30. return isSticky;
  31. };