Syntax MutationObserver - built-in object that observes a DOM and fires a callback when it changes const observer = new MutationObserver(callback) observer.observe(node, [options]) // Configures the MutationObserver for receiving notifications through its callback when DOM changes matching the given options occur node // A DOM Node to watch for changes options = { childList: true, // changes in the direct children of node subtree: true, // in all descendants of node attributes: true, // attributes of node attributeFilter: [arr], // an array of attribute names, to observe only selected ones characterData: true, // whether to observe node.data (text content) attributeOldValue: true, // pass both the old and the new value of attribute to callback characterDataOldValue: true, // pass both the old and the new value of node.data to callback } after any changes, the callback is executed changes are passed in the first argument as a list of MutationRecord objects observer itself goes to the second argument callback = function(changes, observer) { changes.type // mutation type: // 1) "attributes" - attribute modified // 2) "characterData" - data modified, used for text nodes, // 3) "childList" - child elements added/removed changes.type // where the change occurred "attributes" / "characterData" / "childList" changes.addedNodes // nodes that were added changes.removedNodes // nodes that were removed changes.previousSibling // the previous sibling to added/removed nodes changes.nextSibling // the previous sibling to added/removed nodes changes.attributeName // the name of the changed attribute changes.attributeNamespace // the namespace (for XML) of the changed attribute changes.oldValue // the previous value, only for attribute or text changes, if the corresponding option is set 'attributeOldValue'/'characterDataOldValue' } Additional methods observer.takeRecords() // gets a list of unprocessed mutation records by a callback observer.disconnect() // stops the observation until observe() is called again // they usually go together Example: observe all changes in body let observer = new MutationObserver(mutationRecords => console.log(mutationRecords)) observer.observe(document.body, { childList: true, // observe direct children subtree: true, // and lower descendants too characterDataOldValue: true // pass old data to callback }) Example: highlight code snippet Code highlight can be down with Prism let observer = new MutationObserver(mutations => { for(let mutation of mutations) { // examine new nodes, is there anything to highlight? for(let node of mutation.addedNodes) { // we track only elements, skip other nodes (e.g. text nodes) if (!(node instanceof HTMLElement)) continue // check the inserted element for being a code snippet if (node.matches('pre[class*="language-"]')) { Prism.highlightElement(node) } // or maybe there's a code snippet somewhere in its subtree? for(let elem of node.querySelectorAll('pre[class*="language-"]')) { Prism.highlightElement(elem) } } } }) let demoElem = document.getElementById('highlight-demo') observer.observe(demoElem, {childList: true, subtree: true}) Observe all changes in body Mutation observer for dev tools Can be useful if we can not investigate dynamically added or removed component in dev tools. Select in dev tools with mouse click desired element we want to check changes in and paste following code into console. const observedNode = $0 const callback = (mutationList, observer) => { for (const mutation of mutationList) { if (mutation.type === 'childList') { console.log('A child node has been added or removed.') return console.log(mutation.target) } if (mutation.type === 'attributes') { console.log(`The ${mutation.attributeName} attribute was modified.`) return console.log(mutation.target) } } } const observer = new MutationObserver(callback) const config = { attributes: true, childList: true, subtree: true } observer.observe(observedNode, config) // observer.disconnect()