UNPKG

svelte-intersection-observer

Version:

Detects when an element enters or exits the viewport using the Intersection Observer API.

128 lines (104 loc) 3.05 kB
import { fromAction } from "svelte/attachments"; export function intersect(node, options = {}) { let { root = null, rootMargin = "0px", threshold = 0, once = false, skip = false, } = options; let observer; const createObserver = () => { if (typeof IntersectionObserver === "undefined") return null; return new IntersectionObserver( (entries) => { for (const entry of entries) { node.dispatchEvent(new CustomEvent("observe", { detail: entry })); if (entry.isIntersecting) { node.dispatchEvent(new CustomEvent("intersect", { detail: entry })); if (once) observer?.unobserve(node); } } }, { root, rootMargin, threshold }, ); }; observer = createObserver(); if (!skip) observer?.observe(node); return { update(newOptions = {}) { once = newOptions.once ?? false; const newSkip = newOptions.skip ?? false; const configChanged = (newOptions.root ?? null) !== root || (newOptions.rootMargin ?? "0px") !== rootMargin || JSON.stringify(newOptions.threshold ?? 0) !== JSON.stringify(threshold); if (configChanged) { root = newOptions.root ?? null; rootMargin = newOptions.rootMargin ?? "0px"; threshold = newOptions.threshold ?? 0; observer?.disconnect(); observer = createObserver(); if (!newSkip) observer?.observe(node); } else if (newSkip !== skip) { if (newSkip) observer?.unobserve(node); else observer?.observe(node); } skip = newSkip; }, destroy() { observer?.disconnect(); }, }; } export function intersectAttachment(getOptions = () => ({})) { return fromAction(intersect, getOptions); } export function createIntersectionGroup(getSharedOptions = () => ({})) { let observer; const callbacks = new Map(); const handleEntries = ( entries, ) => { for (const entry of entries) { const target = entry.target; const nodeOptions = callbacks.get(target); nodeOptions?.onobserve?.(entry); if (entry.isIntersecting) { nodeOptions?.onintersect?.( ( entry ), ); if (nodeOptions?.once) observer?.unobserve(target); } } }; function attach(nodeOptions = {}) { return ( node) => { if (!observer && typeof IntersectionObserver !== "undefined") { const { root = null, rootMargin = "0px", threshold = 0, } = getSharedOptions(); observer = new IntersectionObserver(handleEntries, { root, rootMargin, threshold, }); } callbacks.set(node, nodeOptions); if (!nodeOptions.skip) observer?.observe(node); return () => { observer?.unobserve(node); callbacks.delete(node); if (callbacks.size === 0) { observer?.disconnect(); observer = undefined; } }; }; } return { attach }; }