Reputation: 2965
Sometimes the mutation observer callback doesn't fire when I expect it to.
If I run this code in developer tools console:
// callback for mutations observer
callbackForAllChanges = function (mutationsList, observer) {
console.log("mutations: ", mutationsList);
};
// create mutation observer
allChanges = new MutationObserver(callbackForAllChanges);
// attach mutation observer to document
allChanges.observe(document, {
childList: true,
subtree:true
});
// create new child
document.body.appendChild(document.createElement("div"));
I would expect callback to fire when I create a new child. But sometimes it does and sometimes it doesn't.
When I run the code in the devtools console while on stackoverflow it works: I see mutations: [MutationRecord]
logged to the console.
When I go on twitter and run the above code in the devtools console, it doesn't seem to work: mutations: [MutationRecord]
is not logged to the console.
What could cause Mutation Observer to not work on twitter?
mutations: [MutationRecord]
does not logconst newDiv = document.createElement('div')
newDiv.setAttribute('id','newDiv')
// callback for mutations observer
callbackForAllChanges = function (mutationsList, observer) {
console.log("mutations: ", mutationsList);
};
// create mutation observer
allChanges = new MutationObserver(callbackForAllChanges);
// attach mutation observer to document
allChanges.observe(newDiv, {
childList: true,
subtree:true
});
// create new child
newDiv.appendChild(document.createElement("div"));
Example
const callbackForAllChanges = function (mutationsList, observer) {
if (mutationsList.find((record) => record.type === "childList")) {
document.body.appendChild(document.createElement("div"));
}
};
I think if this happens it might trigger the mutation work to stop working.
Upvotes: 8
Views: 1240
Reputation: 11
I am facing the same problem, for now I patched the callback by using a loop and checking if some changes were made manually.
setInterval(() => {
const recs = allChanges.takeRecords()
if (recs.length === ) return
yourcallback(recs)
}, 30)
Upvotes: 1
Reputation: 1
This answer dovetails on the response from mayfield which is excellent. I agree that this is a Chromium Devtools bug, reproducible with the steps that he mentioned in his answer. setTimeout() seems to work (albeit inconsistently) to mitigate this. I wonder if this bug might also impact other observers as well ...
I wanted to avoid refactoring a lot of code so I came up with this hacky solution to wrap the native MutationObserver class in your own anonymous class (needs to be run prior to any observers being instantiated):
function fixObserver(name) {
let original=window[name];
window[name]=class {
constructor (cb) {
return new original((...params) => {
setTimeout(() => cb(...params));
});
}
};
}
fixObserver('MutationObserver');
fixObserver('ResizeObserver');
fixObserver('IntersectionObserver');
Upvotes: 0
Reputation: 59
I'm able to reproduce this with these specific circumstances:
Other anecdotes:
takeRecord()
works as expected. As in, it was full of mutation records.queueMicrotask(<cb>)
to decouple your work from the mutation observer callback does NOT resolve the issue.setTimout(<cb>, 0)
DOES resolve the issue.So if you're experience the same issue that I am, the workaround will be to wrap your mutation observer callback in a setTimeout()
as follows...
new MutationObserver(() => setTimeout(myHandler, 0));
I don't feel comfortable calling this an answer to be honest. I believe there is a heisen-bug in Chromium here. If you never open Dev Tools this workaround is superfluous.
Upvotes: 0