VIEW THIS AS

Auto mode follows the Route Engine until you choose a viewpoint.

YOU ARE HERE

ROUTE CHECK

CONNECTED TO

WHAT NEXT

Use the canonical route for this room, or HELP if you are unsure.

The Gold Standard of Websites | Tracing MutationObserver Records After a Subtree Is Removed

Removing a card from an observed container does not always stop that container's MutationObserver from receiving records about the card immediately. With subtree observation, a temporary registration can preserve observation of the removed branch until the observer's next notification processing. This closes a small gap between removal and delivery. It does not make the observer follow that branch indefinitely.

The useful question is therefore not simply, “Is the card still inside the container?” Ask when it was removed, which registration could observe it, which mutation types were requested, and whether the observer's notification processing has already removed the temporary registrations. A record about a detached node can be completely consistent with the DOM rules.

This lesson follows one card and one badge through that boundary. It distinguishes removing a child node from removing an attribute, and temporary subtree coverage from directly observing the card. Those distinctions matter when debugging component teardown, moving content between panels, or deciding when monitoring has actually stopped.

This specialist guide belongs to the Websites root. It also connects to The Gold Standard of Education | Learning That Transfers into Life and the How X Works hub. The worked cases are original teaching examples; their numbers are stipulated so that the reasoning can be checked.

eduKateSG · Websites

Find your next learning step

A detached branch can remain temporarily observable. Follow its registration path and notification boundary before interpreting a late record. The examples are predictions from the declared DOM rules, with a reproducible plan for target-browser checks.

ROUTE 1 · CHAPTERS 1–3

Follow the removed card

Establish the tree, registration and original removal-and-mutation ledger.

ROUTE 2 · CHAPTERS 4–6

Find the lifetime boundary

Separate notification cleanup, observation options and direct component observation.

ROUTE 3 · CHAPTERS 7–9

Trace queues and moves

Work queue draining, cross-container movement and microtask order.

ROUTE 4 · CHAPTERS 10–12

Test the difficult cases

Follow nested removals, shadow boundaries and a falsifiable test plan.

ROUTE 5 · CHAPTERS 13–15

Diagnose and explain

Keep diagnostics proportionate, answer transfer questions and state a bounded conclusion.

Full chapter contents · Websites subject guide

Chapter contents

Follow the removed card · Chapters 1–3

1. Begin with the tree you actually observe

2. Removing a node changes parentage, not identity

3. Work the central removal-and-mutation sequence

Find the lifetime boundary · Chapters 4–6

4. Separate record collection from callback delivery

5. Change one option at a time

6. Direct observation creates a different lifetime

Trace queues and moves · Chapters 7–9

7. Why takeRecords is not the same as disconnect

8. Moves between containers need two separate maps

9. Put the microtasks in order

Test the difficult cases · Chapters 10–12

10. Follow a removal inside an already removed branch

11. Keep shadow-tree boundaries separate from removal timing

12. Build a small test that can falsify the explanation

Diagnose and explain · Chapters 13–15

13. Make teardown evidence useful without recording private content

14. Try the changed-context questions before reading the answers

15. State what the trace supports

CHAPTER 1 OF 15 · Follow the removed card

1. Begin with the tree you actually observe

Back to contents

Imagine a page containing a board. Inside the board is a card; inside the card is a badge. The JavaScript variables board, card and badge refer to those three node objects. We install one observer on board with childList, attributes and subtree set to true. We also request attributeOldValue and restrict attribute records to data-state.

There are two different relationships here. The tree says card is a child of board and badge is a descendant of board. The registration says a particular observer is interested in selected mutations on board and throughout its subtree. The observer is not a camera continuously photographing the page. It receives records when matching DOM algorithms queue them, and its callback runs later.

Before removing anything, set the badge's data-state from idle to ready. This is an attribute mutation on badge. Because badge is in the observed subtree, attributes is enabled and data-state passes the filter, the observer receives a corresponding record. The oldValue field holds idle because old values were requested. The record target is badge, not board: the observation registration and the affected node are different objects.

Now change the badge's title attribute. That mutation does not pass our attribute filter, so it does not produce an attribute record for this registration. The badge is still in the tree. Missing records can therefore result from filtering without any removal at all. Establish this ordinary baseline before introducing detachment; otherwise a filter mistake can masquerade as a lifecycle problem.

The same baseline also separates child-list observation from attribute observation. Enabling childList means changes to a node's children can be reported. It does not mean all changes to those children's attributes are automatically reported. Our example enables both categories deliberately, so later differences can be attributed to timing and registration rather than an omitted mutation type.

Contents · Next chapter

CHAPTER 2 OF 15 · Follow the removed card

2. Removing a node changes parentage, not identity

Back to contents

Call card.remove(). After the call returns, card.parentNode is null, assuming no other code reinserted it. The badge is still a child of card. Removing the card from the board has not dismantled the internal card–badge relationship, and it has not destroyed either JavaScript object.

This is why code can immediately call badge.setAttribute after removing card. A detached node can still have attributes, children, event listeners and programmatic mutations. Whether a particular observer receives those mutations is a separate question. “Detached” describes the tree relationship; it is not a universal switch that makes all DOM activity impossible.

The board's child-list record describes the removal from board. Its target is board, and its removedNodes collection contains card. That record is different from a subsequent attribute record whose target is badge. Keep those targets visible in a trace. If a log prints only record.type, two fundamentally different events can look like one unexplained sequence.

A useful first ledger has three rows: before removal, card.parentNode is board; after removal, card.parentNode is null; throughout both states, badge.parentNode is card. None of those rows tells us whether the observer callback has run. Parentage changes synchronously during the removal operation, whereas notification occurs through the observer mechanism later.

Also distinguish connectedness from containment by the chosen root. A card moved from board to another connected panel may remain connected to the document while no longer belonging to board. Conversely, an observer can be registered on a detached board. A check using isConnected answers a different question from board.contains(card). Use the one that matches the debugging claim.

Contents · Previous chapter · Next chapter

CHAPTER 3 OF 15 · Follow the removed card

3. Work the central removal-and-mutation sequence

Back to contents

Start with badge data-state equal to idle and an empty observer record queue. In one synchronous stretch of script, perform card.remove(), then set badge data-state to ready. Do not insert an awaited timer or notification callback between those two operations.

The removal can queue a child-list record on board. The DOM also provides temporary subtree-observation machinery associated with the removal. That machinery allows a matching later mutation inside the removed branch to remain observable before the relevant temporary registrations are cleaned up. Our second operation can therefore queue an attribute record on badge even though badge is no longer a descendant of board at that moment.

At callback time, the delivered list can contain the board removal record followed by the badge attribute record. The first record says which child left board. The second preserves the badge's previous data-state. A live read of badge.getAttribute('data-state') in the callback returns ready in this simple case. A live read of card.parentNode returns null. Those observations agree: the mutation happened after detachment, within the temporary observation window.

The surprising result comes from combining facts measured at different points. Someone sees parentNode null in the callback and concludes that every record delivered there must have been queued while the card was attached. That conclusion does not follow. The record queue contains historical events; parentNode reports current parentage. The temporary registration explains why this particular historical event remained eligible after the parentage change.

Now change the sequence. Remove card, let the relevant observer notification processing finish, and only then change the badge to ready while card remains detached. With no direct registration on card or badge and no other observed ancestor covering them, the board registration should no longer supply attribute coverage for that later change. The changed condition is notification cleanup, not a different attribute name or a different node object.

StepTree stateRelevant evidence
Observe boardCard is inside boardSubtree attribute and child-list options installed
Remove cardCard is detached; badge remains in cardBoard removal record; temporary coverage can remain
Change badge synchronouslyBadge is still outside boardMatching attribute record can be queued before cleanup
Process notificationCard remains detachedTemporary coverage is removed before this observer callback
Change badge laterNo covering ancestor or direct registrationThe board registration no longer supplies coverage
Predicted lifecycle for the isolated central case, with no independent direct registration.

Contents · Previous chapter · Next chapter

CHAPTER 4 OF 15 · Find the lifetime boundary

4. Separate record collection from callback delivery

Back to contents

A record becomes relevant to an observer when the mutation is processed and matches the registration rules. Delivery is the later step in which queued records are supplied to the callback. The gap matters because several mutations can occur before delivery, and because registration cleanup has its own place in notification processing.

For our removal example, write a numbered execution log: registration installed; card removed; badge changed; observer notification begins; queued records copied for delivery; temporary registrations removed; callback invoked. The exact internal algorithm should be checked against the linked current DOM Standard, but this ordering exposes the important boundary: temporary coverage is removed before that observer's callback runs.

Consequently, a mutation performed inside that callback on the still-detached badge is not covered merely by the temporary registration that allowed the earlier badge mutation. That temporary coverage has already been cleaned up for the observer. A separate direct registration could still make the callback's mutation observable, so the conclusion must state that no such registration exists.

Avoid promising “one callback per removal.” Notification delivers queued records according to the platform's processing rules. Several removals and attribute changes may arrive in one list. A callback may also queue further mutations that lead to later notification work. Count records, callbacks and registrations separately; each count answers a different question.

This is also why a timestamp on the callback is not the timestamp of every mutation it receives. When debugging a race, log the operation boundaries in the script as well as delivery time. If you retain only the callback log, you can discover which records arrived together but cannot reliably reconstruct every intervening application action.

Contents · Previous chapter · Next chapter

CHAPTER 5 OF 15 · Find the lifetime boundary

5. Change one option at a time

Back to contents

Repeat the central example with subtree false. The observer watches mutations on board itself, not all descendants. The removal of card is a child-list change on board and can still be reported. An attribute change on badge is outside the registration's ordinary descendant coverage, and the subtree-based temporary extension is not the mechanism to expect in this configuration.

Next restore subtree true but set attributes false and omit attributeOldValue and attributeFilter. Keep childList true. The removal can be reported, but the data-state change is not an enabled mutation category. This is not evidence that temporary registration cleanup happened early. The mutation simply fails the type test.

For a third comparison, request attributes and subtree but omit childList. Do not reason that an undelivered removal record necessarily keeps the branch under observation forever. The current DOM rules arrange temporary-registration cleanup through observer notification processing even when no matching child-list record is queued for that observer. The callback itself need not be invoked with an empty list merely to perform that cleanup.

This last case is useful because it separates internal notification processing from the visible callback. A debugger that treats “my callback did not run” as proof that no observer-related processing happened will miss the cleanup boundary. Check the standard's current algorithm and test the case directly rather than building the explanation around a callback you may never receive.

Finally enable a data-state filter again and mutate title after detachment. No matching attribute record is expected from that filter. The temporary registration carries the observation options; it is not blanket permission to observe every mutation. A lifetime rule and a filtering rule both have to pass.

Contents · Previous chapter · Next chapter

CHAPTER 6 OF 15 · Find the lifetime boundary

6. Direct observation creates a different lifetime

Back to contents

Create a second observer directly on card with attributes and subtree enabled. Removing card from board does not remove this ordinary registration from card. It is attached to the node, not to the continuing fact that card belongs to board.

Now remove card, wait until the board observer's temporary coverage has ended, and change the badge. The directly registered card observer can still receive a matching badge attribute record. If the board observer receives nothing while the card observer receives one record, there is no contradiction. Their reference nodes and registration lifetimes differ.

This comparison gives a practical design choice. If monitoring should follow a component wherever it moves, a registration on that component may fit the intended lifetime. If monitoring should concern only membership of a panel, an observer on the panel may be appropriate, with the temporary removal window understood. Neither design is universally better; each needs an explicit stop condition.

Be particularly careful when the same MutationObserver object has been registered on several nodes. Seeing one callback does not tell you which registration supplied coverage for every record. Store the observed roots and options alongside the trace. A callback input identifies the mutation target and fields, but it is not a complete history of all registration paths that made the observer interested.

A useful changed-context check is to disconnect only the board observer while leaving the directly registered card observer active. The latter should remain independent. Then disconnect the card observer and repeat the mutation. This pair of experiments distinguishes a deliberately persistent component monitor from a forgotten registration.

Contents · Previous chapter · Next chapter

CHAPTER 7 OF 15 · Trace queues and moves

7. Why takeRecords is not the same as disconnect

Back to contents

takeRecords returns the records currently in an observer's queue and empties that queue. It does not express the same lifecycle decision as disconnect. It is a queue operation, so use it when the goal is to retrieve pending evidence synchronously and then account for it yourself.

In the central example, remove card, call takeRecords to retrieve the removal record, and then mutate badge before the pending notification processing has removed temporary registrations. A later matching mutation can still be observed through the temporary mechanism. Draining the existing queue has not, by itself, ended every registration involved.

This is an excellent near-miss for a teardown bug. A developer drains the removal record and assumes monitoring has finished because the queue is now empty. The badge mutation occurs next and refills the queue. The empty queue described one instant; it did not prove a permanent absence of future matching mutations.

disconnect has broader consequences under the current DOM algorithm: it removes registrations for the observer, removes its temporary registrations, clears its node list and empties its record queue. Calling it before retrieving pending records can discard evidence you intended to retain. If the application requires the final queued records, the order of draining and disconnecting is therefore part of the design.

Write that order explicitly in the teardown function. For example, gather the pending records, disconnect, then process the gathered array under a policy that forbids reinstalling observation accidentally. This is an application pattern to test, not a universal teardown prescription. Some components intentionally reconnect; others should stop permanently. The code needs to state which lifetime it implements.

Contents · Previous chapter · Next chapter

CHAPTER 8 OF 15 · Trace queues and moves

8. Moves between containers need two separate maps

Back to contents

Suppose boardA and boardB are both connected to the document. Move card from A to B synchronously, then change badge. Observer A may still have temporary coverage from the removal, while observer B may have ordinary subtree coverage after insertion. A matching mutation can be relevant to both observers during that interval.

Do not label the result a duplicated user action just because two monitoring systems log it. There is one DOM mutation and two interested observers. If analytics counts both raw logs as separate semantic events, the overcount belongs to the analytics design, not to two user clicks that the DOM records somehow prove occurred.

Draw a map for membership and a second map for registration coverage. Membership places card under B after the move. Coverage can show A's temporary path and B's ordinary path together until A's cleanup. Once that boundary has passed, the ordinary B path remains if its registration is still installed.

The same MutationObserver object used on both boards adds another distinction: an observer's interest map prevents treating multiple matching ancestor registrations as separate observers for one queued mutation. Two observer objects and two registrations on one observer object are different test configurations. Preserve object identity when comparing their record counts.

For a practical transfer check, move card back to A before either callback and change the badge again. A final containment check alone cannot explain the earlier paths. A defensible log records the move sequence and mutation order, not simply the card's last parent. Do not infer that the final parent owned every change in a batched callback.

Contents · Previous chapter · Next chapter

CHAPTER 9 OF 15 · Trace queues and moves

9. Put the microtasks in order

Back to contents

The phrase before the next callback is convenient, but it can conceal the boundary we need to test. Mutation-observer notification is scheduled through a microtask. Another microtask that changes the badge may run before that notification or after it, depending on when it was queued. Being in the same outer task is not enough to make two examples equivalent.

Consider sequence P. First queue a microtask that will change the badge from idle to ready. Then synchronously remove card. When the current script finishes, the previously queued badge-changing microtask is ahead of the removal-triggered notification microtask in this deliberately isolated case. The badge change therefore occurs while the temporary coverage is still available. The notification can receive the removal and attribute records together.

Now consider sequence Q. Synchronously remove card first, then queue the badge-changing microtask. With no observer microtask already pending before this experiment, removal schedules notification ahead of our later microtask. That notification processes the observer and removes its temporary registrations. The badge change then happens on the still-detached branch after cleanup, so the board registration no longer supplies coverage for it.

Both sequences use queueMicrotask and both remove the same card. The decisive difference is enqueue order. To make a reproducible test, state the clean starting assumption: no other mutation notification is already pending for this observer. Otherwise an earlier mutation elsewhere can change the queue arrangement and invalidate the simplified ordering diagram.

Do not replace these steps with an arbitrary sleep measured in milliseconds and call it proof of the algorithm. A later timer task is useful for observing a settled result, but a large delay does not explain which microtask ran first. Log explicit labels when queuing work, when each microtask begins and when the callback runs. That record supports a causal explanation instead of a timing guess.

Contents · Previous chapter · Next chapter

CHAPTER 10 OF 15 · Test the difficult cases

10. Follow a removal inside an already removed branch

Back to contents

The temporary mechanism is easiest to misunderstand when a component has more than one nesting level. Extend the tree to board → card → badge → text. Remove card from board, then remove badge from card before notification processing. Finally alter the text node's data while keeping a reference to that text node.

For this experiment, enable characterData as well as childList and subtree on the board observer. The first removal creates temporary coverage on the removed card branch. The second removal must be analysed using the registrations available on card at that point. The DOM algorithm preserves the source of temporary registrations, so nested removals can continue the temporary observation path into the newly detached badge branch before cleanup.

A useful ledger has four columns: operation, actual parent after operation, registration path, and record category. After the first removal, card has no parent but badge still belongs to card. After the second, badge has no parent but text still belongs to badge. The final character-data change targets text. Neither card nor badge has been destroyed; only the parent links have changed.

This is a conditional prediction from the declared options and algorithm. If characterData is disabled, the last change does not become an attribute record or child-list record merely because it changes what text a person might see. Mutating a text node's data and replacing a node's children are different operations. A test using textContent may exercise a different mutation category from a test directly changing Text.data.

Repeat the nested sequence with notification completed between the two removals. The original temporary path should already have ended, so the later detached operations require their own direct registration or another covering observer. This changed-context case tests the lifetime itself rather than adding another example inside the same window.

Contents · Previous chapter · Next chapter

CHAPTER 11 OF 15 · Test the difficult cases

11. Keep shadow-tree boundaries separate from removal timing

Back to contents

Subtree observation follows the relevant DOM tree relationships; it is not a general promise to inspect every node contributing pixels to the component. An observer on a host does not automatically become an observer inside its shadow root. Removing the host cannot temporarily extend coverage into a tree that the original registration never covered.

Suppose our card is a custom-element host. Its light-DOM badge is one child, while a second badge lives inside an open shadow root. Observe the outer board with subtree and attributes enabled. Change data-state on both badges before any removal. The light-DOM mutation is within the board's tree; the shadow-root mutation requires a registration that actually covers that shadow tree. This baseline should be established before testing either badge after host removal.

An open shadow root lets cooperating code obtain the root and register an observer there. That is an explicit observation decision. The registration's lifetime then needs analysis in its own right; it does not disappear simply because the host leaves the document. A closed root is not made externally observable by requesting subtree on the host.

Slotting adds a related visual trap. A node displayed through a slot does not necessarily acquire the parent relationship that its rendered position suggests. Inspect parentNode and the observation root, and use the DOM tree required by the algorithm. A screenshot of the component cannot establish the registration path.

For application consumers, documented component events often communicate the intended state change more clearly than scraping internal mutations. A dropdown-closed event and a removed child record answer different questions. Prefer the component's supported contract when the reader's job is to know whether the dropdown has closed; use mutation observation when the actual DOM change is the evidence required.

Contents · Previous chapter · Next chapter

CHAPTER 12 OF 15 · Test the difficult cases

12. Build a small test that can falsify the explanation

Back to contents

Create a new isolated board for every case. Give card and badge stable test names, set badge data-state to idle before observation starts, and retain references to all three nodes. Store record.type, a synthetic target name, attributeName, oldValue and the names of removed nodes. Store current parent separately at callback time. Avoid logging complete elements because developer consoles may display their later live state.

Run the central case: observe board, remove card, set badge to ready synchronously, then inspect the delivered records after a later timer task. Under the declared options, the predicted list includes a child-list record targeting board and a data-state attribute record targeting badge. The attribute old value should be idle. If the result differs, first inspect the options and whether a notification boundary was introduced inadvertently.

For the second case, wait for removal notification before changing the badge. The board observer should supply the removal record but not the later detached badge attribute record. For the third, call takeRecords immediately after removal and then change the badge synchronously. Preserve the drained removal record separately; the later callback can still receive the badge record before temporary cleanup ends.

For the fourth, disconnect immediately after removal and before the badge change. Pending records are discarded by disconnect, and this observer should provide neither the pending removal nor the later badge mutation through the removed registrations. For the fifth, add a direct observer on card and repeat the after-notification mutation. Its independent receipt demonstrates why detachment alone does not mean every observer stops.

Record browser name and version, exact options, operation order and observed arrays. These cases are predictions to test, not a claim that every target browser has been exercised here. A mismatch is useful evidence: reduce it to the smallest case before attributing it to a framework or declaring the platform unreliable.

Contents · Previous chapter · Next chapter

CHAPTER 13 OF 15 · Diagnose and explain

13. Make teardown evidence useful without recording private content

Back to contents

A production diagnostic does not need the full HTML of every removed component. For a fictional booking card, log synthetic component identifier C17, observer identifier O2, record category, attribute name, sequence number and the relevant lifecycle labels. Use invented state tokens in a development test. Avoid collecting typed names, contact details, access tokens or complete text content merely to show that a branch was removed.

The minimum useful trace depends on the question. To test whether coverage survives removal, operation order and the presence of a matched attribute record may suffice. To reconstruct a state transition, old values may be required, but that does not justify recording all attributes. Restrict both the observation filter and the diagnostic output to the values needed for the investigation.

Retaining MutationRecord objects can also retain references to node objects. A long-lived array of records may preserve more component state than a compact extracted log. Extract the diagnostic fields you need, apply a retention policy appropriate to the application, and stop observation when its intended lifetime ends. Do not treat an empty-looking detached card as automatically irrelevant to memory or privacy.

A useful teardown review asks three concrete questions. Which object owns the observer? Which event ends its lifetime? What happens to records queued just before that event? If the answers are ambiguous, a delayed callback can process a component after its application state has already been disposed. Decide whether pending evidence should be processed, discarded or transferred before writing the cleanup code.

This article explains DOM observation, not an entire component framework's lifecycle. Framework cleanup hooks, application event queues and network callbacks add their own ordering. Keep the browser trace intact, then connect it to the framework's documented lifecycle instead of assuming that a hook name such as unmounted proves every asynchronous reference has vanished.

Contents · Previous chapter · Next chapter

CHAPTER 14 OF 15 · Diagnose and explain

14. Try the changed-context questions before reading the answers

Back to contents

Question one: the observer watches only childList and subtree. The card is removed and its badge's data-state changes synchronously. Which enabled category would let this observer report that attribute change? There is none. The removal may be reported, but temporary coverage does not enable a mutation category omitted from the options.

Question two: a removal record is returned by takeRecords, then an attribute changes in the detached branch before notification cleanup. Has draining the queue necessarily ended observation? No. The queue and registration lifetime are separate. The returned record is evidence you must account for manually, while temporary coverage can still admit a later matching mutation.

Question three: an observer registered directly on card reports a badge change long after removal notification. Does this contradict the temporary-lifetime rule for the board observer? No. Direct registration on card has its own lifetime. Inspect which observer object and which registration supplied the evidence before comparing callback logs.

Question four: two different observers receive one badge mutation during a move from board A to board B. Can the application infer two user actions? No. Multiple observers can be interested in the same DOM mutation. A semantic action count needs a defined application event, not the sum of every monitoring system's raw records.

Question five: the callback prints card.parentNode as null. Does that prove every record in the list was queued after removal? No. Parentage is being read at callback time. Some records may precede removal and others may follow it within the temporary window. Use the recorded operation order to distinguish them.

Question six: a badge inside a shadow root is missing from the board observer's records. Should adding a longer delay fix it? No. Waiting does not supply a missing registration across a shadow-tree boundary. First verify baseline coverage while the host is still attached, then test the removal lifetime only for registrations that actually exist.

Contents · Previous chapter · Next chapter

CHAPTER 15 OF 15 · Diagnose and explain

15. State what the trace supports

Back to contents

A useful report can be short: “Observer O2 was registered on board with attribute and child-list subtree observation. The card was removed and its badge's data-state changed synchronously before notification cleanup. O2 received the board removal record and the badge attribute record. After cleanup, a further detached badge change was outside that board registration's coverage.” Each sentence identifies an operation or boundary rather than calling the observer delayed or broken.

If you have not run the case in a target browser, change the wording to a prediction from the current algorithm and supply the test conditions. If the trace contains direct registrations, include them. If takeRecords or disconnect was called, include its position in the sequence. A missing lifecycle event is often more important than another timestamp.

The practical learning goal is the ability to predict the changed case. Remove subtree, change the mutation category, move the notification boundary or add direct observation. A learner who can explain which record should change has understood the mechanism. A learner who remembers only that removed nodes are sometimes observed still needs the registration map and execution ledger.

Return to the attribute-old-value guide when the uncertainty concerns historical values versus the live target. Return to the ResizeObserver guide when the uncertainty concerns later layout geometry. Mutation records describe matching DOM changes; they do not establish a new rendered size, a completed animation, a user-visible frame or a successful application task.

Contents · Previous chapter · Return to Websites

Historical attribute values and live nodes

Historical attribute values and live nodes. Use the historical-value lesson when oldValue and a callback-time attribute read disagree. This guide instead follows registration coverage after removal.

Layout-size observation and feedback

Layout-size observation and feedback. Use the delivery-loop lesson when callback writes change observed geometry. Mutation records do not establish that layout delivery completed.

Choose the observed layout box

Choose the observed layout box. Use the box-size lesson after identifying a genuine layout measurement question, keeping padding and border effects separate from DOM mutation evidence.

Frequently asked questions

Does removing a node stop all observers immediately?

No. A direct registration can remain on the removed node, and an ancestor subtree registration can have temporary coverage until notification processing removes its transient registrations. These are separate mechanisms with different stop conditions.

Does takeRecords stop the observer?

No. It returns and clears the records currently queued. It does not have the registration-removal effect of disconnect. Record the queue drain in your trace, because otherwise a missing callback record can look like an unobserved mutation.

Why did the callback receive a record about a node that is no longer inside the board?

The callback receives queued historical evidence. The mutation may have occurred before removal or during temporary coverage after removal. Current containment at callback time cannot, by itself, locate the mutation in that sequence.

Can a filter hide an event even inside the temporary window?

Yes. Temporary coverage carries the applicable options; it does not enable every mutation category or remove an attribute filter. Test matching and nonmatching attribute names while holding the removal sequence fixed.

Is this a ResizeObserver loop problem?

No. MutationObserver handles matching DOM mutation records, while ResizeObserver concerns observed layout-box changes. A DOM change may later affect layout, but the two APIs have different delivery mechanisms and evidence. Diagnose each boundary separately.

What should I retain in a bug report?

Keep a minimal tree, observer identities and options, the exact ordered operations, any manual queue drains or disconnects, and the resulting record fields. Include the browser version. Use synthetic data so that proving the ordering does not require collecting private page content.

Authoritative sources and further reading

WHATWG DOM Standard: mutation observers. Normative definitions of registrations, transient coverage, notification cleanup, record queues and observer methods. Consult the living version when implementing the tests.

WHATWG HTML Standard: microtask queue. Event-loop context for the explicitly ordered microtask examples; queue order is part of each worked case.

Return to the Websites subject guide, The Gold Standard of Education, or the How X Works hub.