JavaScript
Completed
-
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)
212: The Resize Observer API
I've been working on this dashboard project where the user can drag and resize panels. I want one specific panel—a data visualization widget—to switch from a detailed table view to a simplified sparkline view whenever its container gets too narrow. Naturally, my first instinct was to go with the old reliable: window.onresize.
The Window Resize Trap
Let's look at how I started. I figured if the window changes size, the panel probably will too. I wrote a quick listener to check the width of my #viz-panel and toggle a CSS class.
const panel = document.querySelector('#viz-panel');
window.addEventListener('resize', () => {
if (panel.offsetWidth < 400) {
panel.classList.add('compact');
} else {
panel.classList.remove('compact');
}
});
At first glance, it works. I shrink the browser window, and the widget snaps into compact mode. Great, right? But then I remembered that the dashboard has a collapsible sidebar. When I click the "Collapse Sidebar" button, the main content area expands, and the #viz-panel grows. But here's the problem: the window size hasn't changed. The browser doesn't fire a resize event, and my widget stays stuck in compact mode even though it now has plenty of room.
Wait, the Window didn't actually move
This is a classic mistake. We often conflate "the viewport changing size" with "an element changing size." In modern web apps, elements change dimensions for a million reasons that have nothing to do with the browser window: CSS transitions, DOM injections, or sidebars sliding in and out. I need a way to watch the element itself, not the window.
I started digging through the MDN docs and found the ResizeObserver API. The idea is that instead of listening to the global window, I can create an observer that "watches" specific elements and notifies me whenever their contentRect changes.
Letting the Element Speak for Itself
Let's pivot the code. Instead of a window listener, I'll instantiate a ResizeObserver. This takes a callback function that runs whenever the observed element's size changes. The callback gives us an array of entries—since one observer can watch multiple elements.
const panel = document.querySelector('#viz-panel');
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
// entry.contentRect gives us the dimensions
const width = entry.contentRect.width;
console.log(`Current width: ${width}px`);
if (width < 400) {
entry.target.classList.add('compact');
} else {
entry.target.classList.remove('compact');
}
}
});
// Now we tell the observer which element to watch
observer.observe(panel);
Now, when I toggle that sidebar, the observer catches it immediately. It doesn't matter why the panel resized—whether it was a window resize, a CSS change, or a JavaScript style update—the observer just cares that the pixels changed. It's much more robust.
Cleaning Up the Mess
One thing I noticed while testing this: if I'm building a single-page app where components get destroyed and recreated, leaving observers running in the background is a recipe for memory leaks. You can't just let them hang around.
If I'm removing the panel from the DOM or navigating to a different view, I need to call disconnect()`. This stops the observer from watching everything and lets the garbage collector do its job.
// When the component is destroyed
observer.disconnect();
One last tip: be careful about changing the size of an element inside its own ResizeObserver callback. If your logic says "if width < 400, add a border" and that border increases the width to 401, you might trigger another resize event, which triggers the logic again, and suddenly you're in an infinite loop. The browser usually detects this and throws an error, but it's something to keep in the back of your mind when writing your layout logic.
📋 Practical Task
The Adaptive Video Player Control Overlay
You are building a custom video player. The player's container is resizable. You need to implement a feature where the "Control Overlay" (a div inside the player) changes its layout based on the player's width:
- If the player width is less than 500px, the controls should be stacked vertically (add the class
"controls-vertical"). - If the player width is 500px or more, the controls should be side-by-side (remove the class
"controls-vertical").
Requirements:
- Create a
ResizeObserverthat watches an element with the ID#video-player. - Inside the observer, target the element with the ID
#controls-overlay. - Apply or remove the
"controls-vertical"class based on thecontentRect.widthof the player. - Ensure your code handles the
entriesarray correctly, as the observer may track multiple elements.
There are no comments for now.