LayoutInteractivity
useIntersectionObserver
A hook that provides information about the intersection of an element with the viewport or a specified ancestor element.
Parameters
Name | Type | Description |
---|---|---|
options | object | Configuration options for the IntersectionObserver. |
Return Values
Name | Type | Description |
---|---|---|
intersection.observed | boolean | A boolean value indicating whether the observed element is currently intersecting with the configured viewport or ancestor. |
intersection.ref | HTMLElement | A reference to the element to be observed. |
Source Code
export const useIntersectionObserver = (options = { threshold: 0.5 }) => { let ref = $state(); let observed = $state(false); let observer = $state(null); $effect(() => { if (ref) { observer = new IntersectionObserver(([entry]) => { observed = entry.isIntersecting; }, options); observer.observe(ref); return () => { if (observer) observer.disconnect(); }; } }); return { get observed() { return observed; }, get ref() { return ref; }, set ref(element) { ref = element; }, }; };
❌ Not Yet Observed
👇 Scroll to the bottom to find the box!
👀 I feel like I'm being watched...
<script> import { useIntersectionObserver } from './useIntersectionObserver.svelte'; let intersection = useIntersectionObserver({ threshold: 0 }); </script> <div class="container"> <div class="observe-badge" class:observed={intersection.observed}> <span>{intersection.observed ? '✅ Element' : '❌ Not Yet'} Observed</span> </div> <div class="tip"> <span class="tip-emoji">👇</span> <span class="tip-text">Scroll to the bottom to find the box!</span> </div> <div class="box" bind:this={intersection.ref}> <span class="tip-emoji">👀</span> <span>I feel like I'm being watched...</span> </div> </div> <style> .container { height: 200px; display: flex; flex-direction: column; align-items: center; gap: 20px; overflow-y: auto; position: relative; } .observe-badge { position: sticky; top: 0; align-self: flex-start; background-color: rgba(242, 59, 46, 0.2); color: rgb(255, 84, 84); padding: 10px 20px; border: 1px solid rgba(242, 59, 46, 0.2); border-radius: 5px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; font-size: 1.2rem; } .observe-badge.observed { background-color: rgba(34, 172, 59, 0.2); color: rgb(74, 238, 104); border: 1px solid rgba(34, 172, 59, 0.2); } .tip { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 300px; } .tip-emoji { font-size: 32px; } .tip-text { font-size: 20px; color: rgba(255, 255, 255, 0.9); } .text { display: block; text-align: center; color: #fff; } .box { background-color: #84ff54; width: 150px; flex: 0 0 150px; border-radius: 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding: 10px; text-align: center; } .box span:last-of-type { font-weight: 700; color: #000; font-size: 15px; } </style>