admin管理员组文章数量:1356905
I'd like to detect when a specific DOMNode is added to the document, from within the context of the creation of that DOMNode.
Here's what I have so far:
function getThingThatFormatsItself() {
const MY_NODE = createNode();
const observer = new MutationObserver(function (records) {
records.forEach(record => {
record.addedNodes.forEach(n => {
if (n === MY_NODE) {
observer.disconnect();
doFormatting();
}
});
})
});
observer.observe(document, {childList: true, subtree: true});
// do formatting stuff that relies on the element being in DOM
function doFormatting() {
console.log(`It's been added.`);
}
return MY_NODE;
}
/* ELSEWHERE IN MY CODE */
// Now that Thing is added to DOM, it can format itself.
// However, this doesn't work unless it's added via setTimeout!
// Also, in the meantime, we are needlessly iterating through every single node added to the document.
$("#Foo").append(getThingThatFormatsItself());
Two issues with this:
- This doesn't work unless there's a
setTimeout
before adding thingNode to the document. It appears.observe()
doesn't take effect immediately. Is this true? - It's absolutely absurd to have to iterate through every node added to the document in between the time Thing is created and added to document.
Is there a way to see when my node is added without having to rely on external callers using setTimeout, and without having to iterate through every single added node in the meantime?
It's really "confusing", to put it nicely, that I can't observe the actual node itself for addition and removal -- only its children nodes. Some design. It's also quite "confusing" that .observe()
seems to be put on the event queue rather than executed immediately.
I'd like to detect when a specific DOMNode is added to the document, from within the context of the creation of that DOMNode.
Here's what I have so far:
function getThingThatFormatsItself() {
const MY_NODE = createNode();
const observer = new MutationObserver(function (records) {
records.forEach(record => {
record.addedNodes.forEach(n => {
if (n === MY_NODE) {
observer.disconnect();
doFormatting();
}
});
})
});
observer.observe(document, {childList: true, subtree: true});
// do formatting stuff that relies on the element being in DOM
function doFormatting() {
console.log(`It's been added.`);
}
return MY_NODE;
}
/* ELSEWHERE IN MY CODE */
// Now that Thing is added to DOM, it can format itself.
// However, this doesn't work unless it's added via setTimeout!
// Also, in the meantime, we are needlessly iterating through every single node added to the document.
$("#Foo").append(getThingThatFormatsItself());
Two issues with this:
- This doesn't work unless there's a
setTimeout
before adding thingNode to the document. It appears.observe()
doesn't take effect immediately. Is this true? - It's absolutely absurd to have to iterate through every node added to the document in between the time Thing is created and added to document.
Is there a way to see when my node is added without having to rely on external callers using setTimeout, and without having to iterate through every single added node in the meantime?
It's really "confusing", to put it nicely, that I can't observe the actual node itself for addition and removal -- only its children nodes. Some design. It's also quite "confusing" that .observe()
seems to be put on the event queue rather than executed immediately.
1 Answer
Reset to default 8MutationObserver callback runs at the end of an event loop cycle during the microtask queue processing phase which occurs after the main code phase has pleted which is why doFormatting() is called after the currently running code pletes (the entire function call stack so to speak).
Unless there's something else in your other code that makes assumptions on doFormatting being called in the current event loop or depends on DOM layout being updated it should be more or less the same as using setTimeout which schedules the callback to run in the next event loop cycle.
The reason MutationObserver accumulates batches of mutations and reports them all in the microtask queue is to provide a much faster observation capability pared to the deprecated synchronous DOM Mutation Events.
Solution 1: use callbacks to run code after doFormatting()
function onNodeAdopted(node, callback) { new MutationObserver((mutations, observer) => { if (node.parentNode) { observer.disconnect(); callback(node); } }).observe(document, {childList: true, subtree: true}); return node; } function getThingThatFormatsItself(callback) { return onNodeAdopted(createNode(), node => { doFormatting(node); console.log('Formatted'); callback(node); }); } $("#Foo").append(getThingThatFormatsItself(node => { console.log('This runs after doFormatting()'); doMoreThings(); })); console.log('This runs BEFORE doFormatting() as MutationObserver is asynchronous')
Solution 2: don't use MutationObserver, instead intercept Node.prototype.appendChild:
const formatOnAppend = (() => { const hooks = new Map(); let appendChild; function appendChildHook(node) { appendChild.call(this, node); const fn = hooks.get(node); if (fn) { hooks.delete(node); // only restore if no one chained later if (!hooks.size && Node.prototype.appendChild === appendChildHook) { Node.prototype.appendChild = appendChild; } fn(node); } return node; } return { register(node, callback) { if (!hooks.size) { appendChild = Node.prototype.appendChild; Node.prototype.appendChild = appendChildHook; } hooks.set(node, callback); return node; }, } })();
Usage:
function getThingThatFormatsItself() { return formatOnAppend.register(createNode(), node => { console.log('%o is added', node); }); }
Other things to try: window.queueMicrotask(callback) instead of setTimeout to enqueue some of the dependent code in the microtask queue. For older browsers there's a simple polyfill right there in the article.
Check
document.contains(MY_NODE)
(won't help if inside ShadowDOM) orMY_NODE.parentNode
instead of enumerating the mutations:new MutationObserver((mutations, observer) => { if (MY_NODE.parentNode) { observer.disconnect(); doFormatting(); } }).observe(document, {childList: true, subtree: true});
This is also more reliable because in a general case the node may be a child of another added node, not as as a separate item in the addedNodes array.
本文标签:
版权声明:本文标题:javascript - Using MutationObserver to detect when a Node is added to document - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743962320a2569256.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论