Observer APIs
Why you should care
Each Observer API solves a modern browser challenge efficiently. Use them together, and you can build highly dynamic UI behavior without any heavy event listeners or performance penalties. This is exactly where the Observer APIs shine.
- IntersectionObserver ā watch visibility and scrolling
- MutationObserver ā watch DOM changes
- ResizeObserver ā watch element size changes
The Intersection Observer
The IntersectionObserver allows you to detect when an element enters or exits the viewport (or any scrollable container).
Itās the foundation of lazy loading, infinite scrolling, impression tracking, and scroll-based animationsāall without janky scroll event listeners firing every few milliseconds.
Implementing Infinite Scroll
A classic use case: automatically loading more content when a sentinel element becomes visible.
const sentinel = document.querySelector("#load-more-trigger");
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
loadMoreItems(); // Your pagination function
}
},
{
rootMargin: "200px", // preload before the user hits the bottom
},
);
observer.observe(sentinel);
This gives you smooth, battery-friendly infinite scroll behavior with minimal code.
The Mutation Observer
MutationObserver watches for DOM changesāadded/removed nodes, attribute updates, and text changes.
This becomes invaluable when dealing with dynamic content, third-party scripts, user input that modifies the DOM, or frameworks generating markup on the fly.
Implementing Dynamic Text Conversion
Suppose you want to let users type text into a content-editable area and automatically convert supported tagsāsuch as :smile: or [b]bold[/b]āinto actual HTML elements.
const editor = document.querySelector("#editor");
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList" || mutation.type === "characterData") {
editor.innerHTML = editor.innerHTML
.replace(/:smile:/g, "š")
.replace(/\[b\](.*?)\[\/b\]/g, "<strong>$1</strong>");
}
});
});
observer.observe(editor, {
childList: true,
characterData: true,
subtree: true,
});
This is how you build lightweight custom text editors without a full framework.
The Resize Observer
ResizeObserver detects changes in an elementās sizeānot the windowās size, but the actual box size of any element you watch.
This unlocks full layout responsiveness at the component level, independent of media queries or global resize listeners.
Changing styles based on element size
Letās say youāre tracking user-resizable elements and want them to become circles if they drop below 150Ć150px.
const box = document.querySelector(".box");
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
if (width < 150 && height < 150) {
box.style.borderRadius = "50%";
} else {
box.style.borderRadius = "0";
}
}
});
observer.observe(box);
Now the element reacts fluidly as the user resizes it.