JavaScript Internals (521 Qs)•Part 8: Modern JavaScript APIs, WeakRef, Proxy & Performance (464 - 521)
How does the Mutation Observer API work?
The MutationObserver API provides a way to watch for changes to the DOM tree, replacing the deprecated mutation events.
Basic usage:
javascript
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
console.log('Type:', mutation.type);
console.log('Target:', mutation.target);
});
});
const config = {
attributes: true,
childList: true,
subtree: true
};
const targetNode = document.getElementById('observed');
observer.observe(targetNode, config);
// Later: stop observing
observer.disconnect();
Observing attribute changes:
javascript
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'attributes') {
const oldValue = mutation.oldValue;
const newValue = mutation.target.getAttribute(mutation.attributeName);
console.log(`${mutation.attributeName}: ${oldValue} → ${newValue}`);
}
});
});
observer.observe(element, {
attributes: true,
attributeOldValue: true,
attributeFilter: ['class', 'data-status']
});
Observing child nodes:
javascript
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
console.log('Added:', node);
});
mutation.removedNodes.forEach(node => {
console.log('Removed:', node);
});
});
});
observer.observe(container, {
childList: true,
subtree: true
});
Practical example - lazy loading images when added to DOM:
javascript
const imageObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'IMG' && node.dataset.src) {
loadImage(node);
}
// Check descendants
if (node.querySelectorAll) {
node.querySelectorAll('img[data-src]').forEach(loadImage);
}
});
});
});
function loadImage(img) {
img.src = img.dataset.src;
delete img.dataset.src;
}
imageObserver.observe(document.body, {
childList: true,
subtree: true
});
Observing text content changes:
javascript
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'characterData') {
console.log('Text changed:', mutation.target.textContent);
}
});
});
observer.observe(textNode, {
characterData: true,
characterDataOldValue: true
});
Configuration options:
javascript
const config = {
attributes: true, // Watch attribute changes
attributeOldValue: true, // Record old attribute values
attributeFilter: ['class'], // Only watch specific attributes
childList: true, // Watch child nodes
subtree: true, // Watch all descendants
characterData: true, // Watch text content
characterDataOldValue: true // Record old text content
};