Skip to content

Instantly share code, notes, and snippets.

@brandonmcconnell
Created October 4, 2023 20:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonmcconnell/7ce35feb1f4207d09671aad9ccea8bf7 to your computer and use it in GitHub Desktop.
Save brandonmcconnell/7ce35feb1f4207d09671aad9ccea8bf7 to your computer and use it in GitHub Desktop.
`stuck` Svelte action
function getStickyRoot(node: Element) {
let current = node.parentElement;
while (current && current !== document.body) {
const computedStyle = window.getComputedStyle(current);
const overflow = computedStyle.getPropertyValue('overflow');
if (overflow === 'scroll' || overflow === 'auto') {
return current;
}
current = current.parentElement;
}
return document.body;
}
function isNumber(value: any): value is number {
// the isFinite() check also suffices for !isNaN()
return typeof value === 'number' && Number.isFinite(value);
}
export function stuck(node: Element) {
const root = getStickyRoot(node);
function detectStuck() {
if (getComputedStyle(node).position !== 'sticky') {
const dirs = ['', 't', 'r', 'b', 'l', 'x', 'y'];
for (const dir of dirs) {
const suffix = dir ? `-${dir}` : '';
node.classList.remove(`stuck${suffix}`);
}
}
const rect = node.getBoundingClientRect();
const rootRect = root.getBoundingClientRect();
const computedStyle = window.getComputedStyle(node);
const top = parseFloat(computedStyle.getPropertyValue('top'));
const right = parseFloat(computedStyle.getPropertyValue('right'));
const bottom = parseFloat(computedStyle.getPropertyValue('bottom'));
const left = parseFloat(computedStyle.getPropertyValue('left'));
const t = isNumber(top) && rect.top - rootRect.top === top;
const r = isNumber(right) && rootRect.right - rect.right === right;
const b = isNumber(bottom) && rootRect.bottom - rect.bottom === bottom;
const l = isNumber(left) && rect.left - rootRect.left === left;
const x = l || r;
const y = t || b;
let isStuck: boolean = false;
for (const [dir, condition] of Object.entries({ t, r, b, l, x, y })) {
if (condition) {
node.classList.add(`stuck-${dir}`);
isStuck = true;
} else {
node.classList.remove(`stuck-${dir}`);
}
}
if (isStuck) {
node.classList.add('stuck');
} else {
node.classList.remove('stuck');
}
}
root.addEventListener('scroll', detectStuck, { passive: true });
window.addEventListener('resize', detectStuck, { passive: true });
return {
destroy() {
root.removeEventListener('scroll', detectStuck);
window.removeEventListener('resize', detectStuck);
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment