← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

IntersectionObserver API

JavaScript HTML5 Intermediate
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate no automated detection ('automated: no') and only loose code patterns (getBoundingClientRect|scroll.*img). Missing unobserve() calls, threshold misconfiguration, and missing rootMargin are runtime/behavioral issues that won't be caught by linters or type checkers — they require manual testing or code review to surface.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix describes a straightforward pattern replacement: swap scroll event + getBoundingClientRect for IntersectionObserver, add rootMargin configuration, and ensure unobserve() calls. This is contained within a single component (lazy-loading or scroll handler) and doesn't require cross-file refactoring.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). IntersectionObserver is scoped to web contexts and typically isolated to specific performance-critical components (lazy loading, infinite scroll, visibility tracking). It doesn't impose a persistent tax across the codebase; once a component uses it correctly, the choice is localized. It doesn't reshape the overall system architecture.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states a major gotcha: developers expect IntersectionObserver to fire synchronously during scroll (like scroll events), but it actually fires asynchronously between frames, batching intersections. This contradicts familiar scroll-event behavior. Additionally, common_mistakes reveal threshold:0 vs threshold:1 confusion and the subtle need for rootMargin preloading—both non-obvious behaviors that trip developers.

About DEBT scoring →

TL;DR

IntersectionObserver fires when an element enters or leaves the viewport — the modern way to implement lazy loading, infinite scroll, and scroll-triggered animations.

Explanation

IntersectionObserver(callback, options) observes elements for viewport intersection. Options: root (default viewport), rootMargin (expand/shrink root), threshold (0–1, fraction visible to trigger). The callback receives IntersectionObserverEntry[] with isIntersecting, intersectionRatio, boundingClientRect. Use cases: lazy load images (observe img, set src when visible), infinite scroll, animate elements on scroll, sticky header detection. Unobserve after first intersection for one-shot triggers. Far more performant than scroll event + getBoundingClientRect polling.

Common Misconception

IntersectionObserver fires synchronously during scroll — it fires asynchronously between frames, batching all intersections in one callback.

Why It Matters

Replacing scroll event listeners with IntersectionObserver eliminates layout thrashing and dramatically improves scroll performance.

Common Mistakes

  • Not calling unobserve() after one-shot events like lazy loading.
  • Using threshold:0 for 'element is fully visible' — that's threshold:1.
  • Not setting rootMargin to preload images slightly before they're visible.

Code Examples

✗ Vulnerable
window.addEventListener('scroll', () => {
    images.forEach(img => {
        const rect = img.getBoundingClientRect();
        if (rect.top < window.innerHeight) img.src = img.dataset.src;
    });
});
✓ Fixed
const observer = new IntersectionObserver((entries, obs) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.src = entry.target.dataset.src;
            obs.unobserve(entry.target); // One-shot
        }
    });
}, { rootMargin: '200px' }); // Preload 200px before visible

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

Added 23 Mar 2026
Edited 5 Apr 2026
Views 76
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 2 pings F 0 pings S 1 ping S 1 ping M 0 pings T 1 ping W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 2 pings F 1 ping S 0 pings S 0 pings M
No pings yet today
No pings yesterday
Amazonbot 7 ChatGPT 6 Google 6 Ahrefs 4 Scrapy 4 Unknown AI 3 Perplexity 3 SEMrush 3 Claude 2 PetalBot 2 Brave Search 2 Majestic 1 Meta AI 1 Twitter/X 1 Bing 1 Applebot 1
crawler 43 crawler_json 3 pre-tracking 1
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
JavaScript javascript The programming language of the browser — it reads and modifies the page (the DOM), reacts to user events, and fetches data without reloading.

JavaScript is the only language browsers execute, so every interactive behaviour on the web goes through it. Its two defining traits — single-threaded event loop and loose typing (== coercion) — explain the majority of both its bugs and its design patterns.

💡 Default to const, use === always, and reach for let only when a value genuinely reassigns.

Ask Codex about JavaScript →
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Low
⚡ Quick Fix
Replace scroll event + getBoundingClientRect with IntersectionObserver. Use rootMargin for preloading. Always unobserve() after one-shot triggers.
📦 Applies To
javascript HTML5 web
🔗 Prerequisites
🔍 Detection Hints
getBoundingClientRect|scroll.*img
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function


✓ schema.org compliant