Answer
The short answer is: no, calling takeRecords after each mutation does not guarantee that every pending MutationRecord will be returned in IE10, and disconnecting the observer during a rollback can lose notifications.
Why this happens
- MutationObserver does not deliver records synchronously. All mutations that occur before the current task finishes are queued as a microtask; the observer’s callback (or a manual
takeRecords call) runs only after that microtask flushes.
- If several DOM changes happen in the same synchronous block (e.g., a loop that removes and then re‑adds nodes), they are batched into a single microtask. Calling
takeRecords after each change while still inside that block will return only the records that have already been queued – which may be none until the block ends.
- Disconnecting the observer before its microtask runs discards the queued records for any mutations that happened while it was disconnected. Re‑connecting later does not recover those lost records.
- The order of items inside each
MutationRecord reflects the order the browser queued them, which matches the chronological order of the original mutations. However, because records can be coalesced, you cannot rely on receiving a separate record for every single attribute or node change.
Steps to guarantee capture of every intermediate mutation during a fast rollback
- Keep the observer connected for the entire duration of the rollback sequence.
- After the synchronous DOM‑change block ends, flush the microtask queue before reading records. The simplest way is to use a zero‑delay promise:
// apply your schema changes and rollback here
Promise.resolve().then(() => {
const records = observer.takeRecords();
// process records to restore the exact original state
});
- If you must temporarily disconnect the observer (e.g., to prevent infinite loops), first flush the microtask queue, disconnect, perform the changes, then reconnect and flush again before calling
takeRecords.
- Do not rely on the observer’s callback alone for immediate action; use the microtask‑flush pattern above or
requestAnimationFrame if you need to run after the next paint.
What to verify
Create a test that rapidly toggles an attribute on an element while the observer stays connected. After the loop, flush with Promise.resolve().then and confirm that takeRecords returns a consolidated list containing all expected mutations. Repeat the test with a disconnect‑reconnect inside the loop; you should see fewer or zero records, confirming the loss.
If you are unsure whether your code already flushes the microtask queue, let me know whether you are using setTimeout, requestAnimationFrame, or a plain synchronous loop – that detail will determine whether the flush step is required.