-
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)
215: Practice Exercise: Building an Infinite Scroll List
I remember a project a few years back where a junior dev on my team was tasked with building a "News Feed" for a client. He went with the most intuitive approach: he added an event listener to the window's scroll event. Every time the user scrolled a single pixel, the browser ran a heavy calculation to check if the user had reached the bottom. The result? The page felt sluggish, the laptop fans started screaming, and the site practically froze on mobile devices. He had accidentally created a performance bottleneck by firing a function hundreds of times per second.
The problem is that the scroll event is synchronous and fires constantly. If you're doing anything complex inside that listener—like calculating offsetHeight or triggering an API call—you're going to kill the frame rate. In modern JavaScript, we have a much more elegant tool for this: the IntersectionObserver API. Instead of asking the browser "Where am I right now?" every millisecond, we tell the browser "Let me know when this specific element becomes visible."
Taming the scroll with IntersectionObserver
The IntersectionObserver is a game-changer because it offloads the visibility logic to the browser's own optimized internals. You create an observer, give it a callback function, and then tell it which element to watch. When that element enters the viewport (or a specific percentage of it does), the callback fires.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log("Bottom reached! Load more data...");
loadMoreContent();
}
});
}, { threshold: 1.0 });
// We tell the observer to watch a specific 'sentinel' element
observer.observe(document.querySelector('#scroll-anchor'));
I usually recommend using a "sentinel" element—a simple, empty div placed at the very bottom of your list. When that div scrolls into view, it's your signal to fetch the next page of data. It's cleaner than calculating coordinates and far more performant.
Preventing the Double-Fetch Glitch
One thing that always trips people up is the "double-fetch." Because the observer might trigger slightly before the new content is fully rendered, or because the user is scrolling quickly, you might accidentally fire off three API requests for the same page of data. This leads to duplicate items in your list and a messy UI.
The fix is simple: use a loading flag. I always wrap my fetch logic in a boolean check. If isLoading is true, the function returns immediately. Only once the data is fetched and appended to the DOM do we set isLoading back to false. It’s a small addition, but it prevents your API from getting hammered and your UI from glitching.
Managing the DOM Append
When you get your new batch of data, you'll be tempted to use innerHTML += .... Don't do it. That forces the browser to re-parse and re-render the entire list every time you add a few items, which defeats the purpose of a performant infinite scroll. Instead, create the elements using document.createElement or use a DocumentFragment to batch your updates. This keeps the browser's main thread happy and the scrolling smooth.
📋 Practical Task
Exercise: Building the Endless Portrait Gallery
Your goal is to create a gallery that loads "portraits" (images) as the user scrolls. You will use a mock API simulation to mimic paginated data fetching.
- Create an HTML structure with a container
#galleryand a sentinel element#sentinelat the bottom. - Implement an
IntersectionObserverthat triggers a function calledfetchImages()whenever the#sentinelenters the viewport. - Inside
fetchImages(), implement a loading flag (e.g.,isFetching = true) to prevent duplicate requests while a "network call" is in progress. - Simulate an API call using a
setTimeoutof 1 second. The "API" should return an array of 10 image URLs (you can usehttps://picsum.photos/200/300?random=Xwhere X is a number). - Append these images to the gallery using a
DocumentFragmentto ensure high performance. - Once the images are appended, reset your loading flag so the next scroll trigger can work.
There are no comments for now.