In modern web development, the content of a page can change dynamically without a page reload frequently modifying the DOM. We often have to respond to these changes in our dynamic web applications. Here are some common real-world scenarios where this is important:
Loading content dynamically: Automatically update the UI when content changes without reloading the page.
Form validation: Monitor form fields for changes to provide real-time validation feedback.
Custom elements: Enhance custom web components by reacting to changes in their attributes or child elements.
Understanding how to effectively handle these dynamic changes is key to building robust and user-friendly web applications. Previously, we used the MutationObserver
interface.
MutationObserver
APIThe MutationObserver
API is an interface provided by most modern browsers to track changes in the DOM tree. It is particularly useful for reacting to modifications of an element and its children, making it essential for creating dynamic and interactive web applications. Let’s explore how we can use it to monitor various changes in the DOM.
We can create an instance of the MutationObserver
interface using the following syntax.
const mutationObserver = new MutationObserver(callbackFunction);
The MutationObserver
constructor takes a callbackFunction
function as its argument, which will be invoked whenever a mutation is detected. This function must accept at least one parameter carrying the detected changes. Following is an example code showing the signature of the callback function.
const callbackFunction = (mutations) => {// react to the DOM changes};
A MutationObserver
object has the following three primary methods:
The observe
method: This starts tracking the target DOM node for the mutations specified in the configuration object. It takes two parameters:
targetNodeInstance
: The instance of the DOM node to be observed.
configObj
: An object specifying the types of mutations to observe and their options.
mutationObserver.observe(targetNodeID, configObj);
The disconnect
method: This stops the MutationObserver
instance from receiving notifications of DOM mutations. This prevents memory leaks or unnecessary performance overhead when monitoring is no longer needed.
mutationObserver.disconnect();
The takeRecords
method: This returns a list of all pending mutations that have been observed but not yet processed by the callback. This method helps retrieve any pending mutation records, providing a way to handle changes that might have occurred before the observer was disconnected or before the callback was triggered.
let pendingMutations = mutationObserver.takeRecords();
Note: Make sure the DOM object is fully loaded before observing the changes using the
MutationObserver
object.
configObj
The following is a list of possible mutations that can be observed using the MutationObserver
API:
Mutation type | Description |
| Monitors changes ot the direct child elements when set to |
| Monitors changes to the entire subtree elements of the target node when set to |
| Monitors changes to the attributes of the target node when set to |
| Records the previous value of the attribute changed when set to |
| Monitors changes to the attributes of the target node from the specified attribute list. |
| Monitors changes to the text content of the target node when set to |
| Records the previous value of the text content changed when set to |
The following is an example of a configuration object that tracks changes to child nodes, attributes, and the entire subtree of the target element.
const configObj = {childList: true,attributes: true,subtree: true};
The MutationObserver
API provides detailed information about changes to the DOM through a list of mutation records. Each mutation record contains various properties that describe the specific change that occurred. Here are the possible properties included in these mutation records:
target
: This is the node on which the mutation occurred. This property is essential as it identifies the specific element in the DOM that was modified.
addedNodes
: This is a NodeList
of nodes that were added to the target
. This list can include multiple nodes if several were added simultaneously.
removedNodes
: This is a NodeList
of nodes that were removed from the target
. Like addedNodes
, this can include multiple nodes if several were removed at once.
oldValue
: This is the previous value of the attribute or text node before the mutation occurred. This property is useful for tracking changes and possibly reverting them if necessary.
attributeName
: This is the name of the changed attribute if the mutation was related to an attribute change. This helps in identifying which specific attribute was modified.
attributeNamespace
: This is the namespace of the changed attribute, if applicable. This is relevant for XML documents or elements using namespaced attributes.
nextSibling
: This is the next sibling of the added or removed nodes. This property is useful for understanding the context of the mutation within the document structure.
previousSibling
: This is the previous sibling of the added or removed nodes. Like nextSibling
, this helps in contextualizing the mutation within the document.
type
: This is a string indicating the type of mutation that occurred. The possible values are: attributes
, characterData
, and childList
.
Understanding these properties helps developers accurately respond to and handle changes in the DOM, enabling the creation of dynamic and interactive web applications.
Let’s understand the above code line by line:
Line 3: We select the DOM element with the ID target
and assign it to the constant targetNodeInstance
. This is the element that will be observed for mutations.
Lines 5–11: We create a callback function that is executed whenever mutations are detected. The callback function iterates over the list of mutation records, logging messages to the console if a child node is added or an attribute is modified.
Line 13: We create a MutationObserver
which takes the previously defined callbackFunction
as its argument.
Lines 15–18: A configuration object named configObj
is defined to specify the types of mutations to observe changes to attributes and changes to the child nodes of the target node.
Line 20: The observer is then started on targetNodeInstance
with this configuration, initiating the observation process.
Lines 23–26: Finally, the script simulates a change in the DOM after a 1-second delay using setTimeout
. Inside the delayed function, a new paragraph element is created, given some text content, and appended to the targetNodeInstance
. It also updates the class name to new-class
.
When the provided code is executed, it will log “A child node has been added.” to the console. This is triggered by the mutation observer detecting the addition of a new child node to the targetNodeInstance
, demonstrating the observer’s functionality.
The following widget demonstrates the detection of change in the attribute of the targetNodeInstance
.
Notice that we have updated line 25 to change the class name of the targetNodeInstance
. If we run the above widget, the console output will be “Attribute class has been changed.”
MutationObserver
is widely supported in modern browsers, including Chrome, Firefox, Safari, Edge, and Opera. However, it is not available in Internet Explorer. For legacy support, you might need to use alternative methods or polyfills.
Supported Methods | Chrome 26 | Edge 12 | Firefox 14 | Safari 7 | Opera 15 |
| Full | Yes | Yes | Yes | Yes |
| Full | Yes | Yes | Yes | Yes |
| Full (May need flags to set) | Full (May need flags to set) | Full (May need flags to set) | Full (May need flags to set) | Full (May need flags to set) |
| Full | Yes | Yes | Yes | Yes |
| Full | Yes | Yes | Yes | Yes |
Quiz
How do you start observing changes with MutationObserver
?
By calling observer.connect()
By calling observer.observe()
By calling observer.start()
By setting the observeChanges
property to true
Free Resources