-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
86: Working with the Intersection Observer API
Imagine you're hiring a security guard to watch a specific gate. You have two choices. You could stand there yourself and stare at the gate every single second of the day, constantly asking, "Is someone there yet? How about now? Now?" That's exhausting, and you can't get anything else done. Or, you can tell the guard, "Hey, I'm going to go grab some coffee. Just ring this bell the moment someone actually walks through that gate."
In the world of JavaScript, the first approach is like attaching a scroll event listener to the window. It fires hundreds of times a second, forcing the browser to recalculate positions constantly, which usually leads to "jank" or laggy scrolling. The second approach—the security guard—is exactly how the Intersection Observer API works. It lets you asynchronously observe changes in the intersection of a target element with an ancestor element or a top-level document's viewport.
Here is how that analogy maps to the code:
- The Gate: This is the target element you want to watch (like a footer or an image).
- The Security Guard: This is the
IntersectionObserverinstance. - The Bell: This is the callback function that runs only when the target enters or leaves the view.
- The Instructions: These are the
options(likethreshold) where you tell the guard, "Don't ring the bell until at least half the person is through the gate."
Stop polling and start observing
I've seen way too many developers struggle with getBoundingClientRect() inside a scroll loop. It's a performance killer. Instead, let's look at how we actually implement an observer. Suppose we have a series of "feature cards" on a landing page that we want to fade in as the user scrolls down to them.
const observerOptions = {
root: null, // Use the browser viewport as the container
rootMargin: '0px',
threshold: 0.2 // Trigger when 20% of the element is visible
};
const observerCallback = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Element is visible!');
entry.target.classList.add('fade-in-visible');
// Once it's visible, we don't need to watch it anymore
observer.unobserve(entry.target);
}
});
};
const observer = new IntersectionObserver(observerCallback, observerOptions);
// Now we tell the observer which elements to watch
const cards = document.querySelectorAll('.feature-card');
cards.forEach(card => observer.observe(card));
Tuning the trigger point
The options object is where you fine-tune the behavior. I usually spend the most time messing with the threshold. A value of 0 means "trigger the moment a single pixel enters the view," while 1.0 means "trigger only when the entire element is fully visible."
Then there's rootMargin. Think of this as a "buffer zone" around the viewport. If you set rootMargin: '200px', the observer will trigger the callback when the element is still 200 pixels outside the viewport. This is a lifesaver for lazy-loading images; you can start loading the image before the user actually reaches it, so it's already there by the time they arrive. It makes the site feel much snappier.
Cleaning up your observers
One thing people often forget is that observers stay active until you tell them to stop. If you're building a single-page app where components mount and unmount, you'll end up with memory leaks if you don't clean up. Use observer.unobserve(element) to stop watching a specific item, or observer.disconnect() to shut down the entire observer and stop watching everything.
In the example above, I used observer.unobserve(entry.target) inside the callback. Since we only want the "fade-in" animation to happen once, there's no reason to keep the guard on duty for that specific card once the animation has triggered.
📋 Practical Task
Build a "Scroll-to-Load" Image Gallery
Your task is to create a simple image gallery that implements lazy-loading using the Intersection Observer API. This prevents the browser from downloading images that the user never scrolls down to see.
Requirements:
- Create an HTML page with at least 5 large image placeholders (use
<div>tags with a background color and a height of 400px to simulate images). - Give each placeholder a
data-srcattribute containing a URL to a real image (you can usehttps://picsum.photos/). - Implement an
IntersectionObserverthat watches these placeholders. - When a placeholder enters the viewport (use a
thresholdof 0.1), change thedivinto an<img>tag (or change thebackground-imagestyle) using the URL fromdata-src. - Ensure that once an image is loaded, the observer stops watching that specific element to save resources.
- Add a
rootMarginof100pxso the images start loading slightly before they hit the screen.
There are no comments for now.