DOMadvanced
MutationObserver and Reacting to DOM Changes
A browser API for observing and reacting to changes in the DOM tree — added/removed nodes, attribute changes, text changes — asynchronously and efficiently.
MutationObserver reports changes to the DOM asynchronously, in batches.
MutationObserver is like a security camera with motion-triggered recording pointed at a specific room (the target node): instead of a guard manually walking back to check the room every few seconds (polling), the camera silently watches and delivers a batched summary of everything that happened the moment there's a natural pause to check the footage (microtask), rather than interrupting everything the instant a single item moves.
Key Concepts
1
const obs = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === 'childList') handleAdded(m.addedNodes);
}
});2
obs.observe(target, {
childList: true, // children added or removed
subtree: true, // and in all descendants
attributes: true,
attributeFilter: ['class'],
characterData: true
});3
obs.disconnect();4
What it replaced. Mutation events fired synchronously on every change and made pages crawl. They were
deprecated for that reason.
5
MutationObserver batches changes and delivers them as a microtask, so a hundred insertions produce one
callback.
MutationObserver
6
Where it is genuinely needed.Reacting to markup you do not control — a third-party widget, an embedded editor, a CMS-injected block.
7
Enforcing something across dynamic content — attaching behaviour to elements that appear later, when
delegation cannot help because it is not an event.
8
Detecting when an element is finally rendered, in a codebase where you cannot hook the render.
9
The infinite-loop trap.
const obs = new MutationObserver(() => {
el.classList.add('seen'); // triggers the observer again
});
obs.observe(el, { attributes: true });10
Disconnect before mutating and reconnect afterwards, or filter so your own change is ignored.
11
Scope it narrowly. subtree: true on document.body with everything enabled reports every change on
the page, and the callback then runs constantly.
subtree: truedocument.body
12
attributeFilter: ['class'] // only this attribute
observe(section, ...) // only this part of the tree13
attributeOldValue and characterDataOldValue must be requested explicitly if you need the previous
value.
attributeOldValuecharacterDataOldValue
14
When not to use it. If you own the code that changes the DOM, call your function there — an observer is
indirection with a cost. And for visibility, use IntersectionObserver; for size, ResizeObserver. Each is
cheaper than watching mutations and inferring.
IntersectionObserverResizeObserver
15
What the interviewer is probing.1. "What replaced mutation events and why?" Probing: the history. Stalls: "Nothing." *Moves
up:* MutationObserver — the old events fired synchronously on every change and were a serious
performance problem.
16
2. "When do the callbacks fire?" Probing: the batching. Stalls: "Immediately." Moves up:
asynchronously as a microtask, with records batched, which is what makes it cheap.
17
3. "What must you configure?" Probing: the options. Stalls: "Just the element." Moves up:
which mutations to watch — childList, attributes, subtree, characterData — since nothing is observed
by default.
18
4. "When would you actually need it?" Probing: honest use cases. Stalls: "Often." *Moves
up:* reacting to DOM you do not control, such as third-party widgets or injected content — your own
framework already tells you.