Alternative To Mutationobserver For Synchronous Notifications
Solution 1:
There's no API other than those you've mentioned. The only additional approach is to hook Node.prototype.appendChild
, and a bunch of other methods to alter DOM in page context. Naturally you'll have to hook things like innerHTML/outerHTML setters as well.
Redefining prototype methods may break some sites that do similar low-level things. Theoretically, at least, so be warned.
Here's a simplified content script that intercepts a few common methods:
const eventId = chrome.runtime.id + Math.random().toString(36);
const script = document.createElement('script');
script.textContent = `(${eventId => {
let reportingEnabled = true;
// only simple data can be transferred, not DOM elements, not functions, etc.
const sendReport = detail => dispatchEvent(new CustomEvent(eventId, {detail}));
const makeHook = (name, fn) =>
function () {
if (reportingEnabled) sendReport({name, phase: 'pre'});
const res = fn.apply(this, arguments);
if (reportingEnabled) sendReport({name, phase: 'post'});
return res;
};
const {appendChild} = Node.prototype;
Node.prototype.appendChild =
Element.prototype.appendChild = makeHook('appendChild', appendChild);
const {append} = Element.prototype;
Element.prototype.append = makeHook('append', append);
const innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
innerHTML.set = makeHook('innerHTML', innerHTML.set);
Object.defineProperties(Element.prototype, {innerHTML});
}})('${eventId}')`;
document.documentElement.appendChild(script);
script.remove();
window.addEventListener(eventId, e => {
console.log(e.detail);
});
Obviously you'll need to hook all the other methods like removeChild, insertBefore, and so on.
DOM elements cannot be transferred via messaging from the page context to the content script. Only trivial types like strings, numbers, boolean, null, and arrays/objects that consist of such types are transferable. There's a trick though for an existing DOM element: you can transfer its index [...document.getElementsByTagName('*')].indexOf(element)
and then use it immediately as document.getElementsByTagName('*')[index]
. For ShadowDOM you'll have to make a recursive indexer.
Post a Comment for "Alternative To Mutationobserver For Synchronous Notifications"