brandonmcconnell

`stuck` Svelte action

Oct 4th, 2023 (edited)
1,374
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 2.38 KB | Source Code | 0 0
  1. function getStickyRoot(node: Element) {
  2.   let current = node.parentElement;
  3.   while (current && current !== document.body) {
  4.     const computedStyle = window.getComputedStyle(current);
  5.     const overflow = computedStyle.getPropertyValue('overflow');
  6.  
  7.     if (overflow === 'scroll' || overflow === 'auto') {
  8.       return current;
  9.     }
  10.     current = current.parentElement;
  11.   }
  12.  
  13.   return document.body;
  14. }
  15.  
  16. function isNumber(value: any): value is number {
  17.   // the isFinite() check also suffices for !isNaN()
  18.   return typeof value === 'number' && Number.isFinite(value);
  19. }
  20.  
  21. export function stuck(node: Element) {
  22.   const root = getStickyRoot(node);
  23.   function detectStuck() {
  24.     if (getComputedStyle(node).position !== 'sticky') {
  25.       const dirs = ['', 't', 'r', 'b', 'l', 'x', 'y'];
  26.       for (const dir of dirs) {
  27.         const suffix = dir ? `-${dir}` : '';
  28.         node.classList.remove(`stuck${suffix}`);
  29.       }
  30.     }
  31.     const rect = node.getBoundingClientRect();
  32.     const rootRect = root.getBoundingClientRect();
  33.     const computedStyle = window.getComputedStyle(node);
  34.  
  35.     const top = parseFloat(computedStyle.getPropertyValue('top'));
  36.     const right = parseFloat(computedStyle.getPropertyValue('right'));
  37.     const bottom = parseFloat(computedStyle.getPropertyValue('bottom'));
  38.     const left = parseFloat(computedStyle.getPropertyValue('left'));
  39.  
  40.     const t = isNumber(top) && rect.top - rootRect.top === top;
  41.     const r = isNumber(right) && rootRect.right - rect.right === right;
  42.     const b = isNumber(bottom) && rootRect.bottom - rect.bottom === bottom;
  43.     const l = isNumber(left) && rect.left - rootRect.left === left;
  44.     const x = l || r;
  45.     const y = t || b;
  46.  
  47.     let isStuck: boolean = false;
  48.     for (const [dir, condition] of Object.entries({ t, r, b, l, x, y })) {
  49.       if (condition) {
  50.         node.classList.add(`stuck-${dir}`);
  51.         isStuck = true;
  52.       } else {
  53.         node.classList.remove(`stuck-${dir}`);
  54.       }
  55.     }
  56.     if (isStuck) {
  57.       node.classList.add('stuck');
  58.     } else {
  59.       node.classList.remove('stuck');
  60.     }
  61.   }
  62.  
  63.   root.addEventListener('scroll', detectStuck, { passive: true });
  64.   window.addEventListener('resize', detectStuck, { passive: true });
  65.  
  66.   return {
  67.     destroy() {
  68.       root.removeEventListener('scroll', detectStuck);
  69.       window.removeEventListener('resize', detectStuck);
  70.     },
  71.   };
  72. }
  73.  
Advertisement