Frontend Master

JavaScript Curriculum

MutationObserver & ResizeObserver
+60 XP

MutationObserver & ResizeObserver

medium
~25 min·60 XP

The coffee shop admin panel uses a third-party widget that modifies the DOM unpredictably. The sidebar needs its own responsive breakpoints independent of the window width. Both problems are solved by the Observer APIs.

MutationObserver & ResizeObserver

The third Observer in the browser toolkit. MutationObserver watches the DOM tree for changes. ResizeObserver watches elements for size changes. Both are async, performant, and replace fragile polling or resize event hacks.

MutationObserver — Watch the DOM Change

Observe DOM mutations in real time — add children, change attributes, modify text:

mutation-observer.js
Observed element
<p class="intro">Hello <strong>World</strong></p>
Mutation records (0)
No mutations yet
const mo = new MutationObserver(
  (mutations) => {
    mutations.forEach(m => {
      console.log(m.type)
      // 'attributes'
      // 'childList'
      // 'characterData'
    })
  }
)
mo.observe(el, {
  attributes: true,
  childList: true,
  characterData: true,
  subtree: true,
})
ℹ️When to use MutationObserver
The most common real-world use: watching for dynamically added elements from third-party scripts, then wiring up your own behaviour to them. Also useful for auto-save — observe an entire form subtree and save whenever anything changes.

ResizeObserver — Element-Level Breakpoints

Watch individual elements resize — not the window:

resize-observer.js
Drag sliders to simulate ResizeObserver callback
300×120pxtablet
Width300px
Height120px
// Drag the handles to resize
const ro = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const { width, height } = entry.contentRect

    // Component-level breakpoints:
    if (width < 240) applyLayout('mobile')
    else if (width < 360) applyLayout('tablet')
    else applyLayout('desktop')
  })
})

ro.observe(document.querySelector('.card-grid'))
// No window resize listener needed!
💡Container queries without CSS Container Queries
ResizeObserver lets you implement component-level responsive design before CSS Container Queries are fully supported. A sidebar can switch from full to compact layout based on its own width — regardless of the window size.

All Three Observers Compared

When to reach for each observer:

observer-patterns.js
MutationObserver

Watches the DOM for changes — added/removed nodes, attribute changes, text content changes.

Use cases
  • Detect when a third-party script modifies the DOM
  • Auto-save form content when any input changes
  • Watch for dynamically added elements to wire up behaviour
  • Implement undo/redo by recording DOM mutations
Gotcha: Can fire thousands of times if watching a busy subtree. Use debounce or disconnect when done.
const mo = new MutationObserver((mutations) => {
  mutations.forEach(m => {
    if (m.type === 'childList') {
      m.addedNodes.forEach(node => {
        if (node.nodeType === 1) {
          wireUpNode(node)
        }
      })
    }
  })
})
mo.observe(document.body, {
  childList: true, subtree: true
})
⚠️Always disconnect observers when done
All three observers hold references to DOM elements — preventing garbage collection. In single-page apps, always call `observer.disconnect()` when the component or element is removed. In React, return a cleanup function from useEffect.

Your Challenge

On .cards container, create a MutationObserver with { childList: true }. When a card is added, log its h3 text. Create a ResizeObserver on .sidebar — toggle class compact when width < 250px. Both should disconnect() when the page navigates away.

Challenge

Wire a MutationObserver on the .cards container to auto-wire click handlers on any newly added cards. Wire a ResizeObserver on a .sidebar element that adds class 'compact' when its width drops below 250px.

MutationObserverobservedisconnectmutationschildListattributessubtreeResizeObservercontentRectcomponent-breakpoints
MutationObserver & ResizeObserver | Nexus Learn