トップへ(mam-mam.net/)

How to Monitor DOM Attribute and CSS Changes in JavaScript|Using MutationObserver

Japanese

How to Monitor DOM Attribute and CSS Changes in JavaScript|Using MutationObserver

This beginner‑friendly guide explains how to use JavaScript’s MutationObserver to monitor DOM attribute and CSS changes in real time.
For example, when the layout of an element on the page changes, you can dynamically adjust other elements accordingly.
With MutationObserver, you can efficiently run processes that adapt your page’s design to DOM updates.
This article provides detailed explanations along with copy‑and‑paste sample code to help you get started.

What Are the Benefits of MutationObserver?

  1. High performance
    Unlike using setInterval() to repeatedly check the DOM, MutationObserver runs only when a change actually occurs, reducing unnecessary processing.
  2. Real‑time updates
    Changes such as CSS updates or element additions/removals are detected immediately.
  3. Fine‑grained monitoring
    You can precisely specify what to observe, including attribute changes (attributes), added or removed child elements (childList), and text changes (characterData).

Cautions and Best Practices When Using MutationObserver

  1. Avoid monitoring too broad a scope
    If you set subtree: true and observe the entire page, the observer may detect unnecessary changes and cause performance degradation.
  2. Be careful when recording previous values
    Options like attributeOldValue: true allow you to capture the value before a change, but monitoring too many targets can increase memory usage.
  3. Properly disconnect the observer
    When navigating away from the page or when the observer is no longer needed, call observer.disconnect() to stop unnecessary monitoring.

Execution Sample

<div id="target_node">This element moves randomly</div>
<div id="chase_node">This element follows the one above when a change is detected</div>

MutationObserver Code Example|Copy & Paste Ready

<div style="position:relative;width:100%;height:300px;">
  <div id="target_node" style="position:absolute;width:240px;height:80px;background:#DFF;">
    This node moves randomly
  </div>
  <div id="chase_node"  style="position:absolute;width:240px;height:80px;background:#FFD;">
    This element follows the one above when a change is detected
  </div> 
</div>

<script>
  let tid;
  window.addEventListener("load",function(){
    
    // MutationObserver settings (monitor changes on #target_node)
    const observer = new MutationObserver(function(mutations){
      // The mutation details are passed into the 'mutations' variable
      // console.log(mutations);

      // Since #target_node has changed, update #chase_node to follow it
      let t = document.getElementById("target_node");
      let c = document.getElementById("chase_node");
      let r = t.getBoundingClientRect();
      c.style.left = (parseInt(t.style.left) + 20) + 'px';
      c.style.top  = (parseInt(t.style.top) + r.height + 10) + 'px';
    });
    
    // Start observing #target_node (detect style changes)
    observer.observe(
      document.getElementById("target_node"), // Specify the target node
      {
        childList: true,            // Monitor addition/removal of child nodes (including text nodes)
        attributes: true,           // Monitor attribute changes (style, id, class, etc.)
        characterData: true,        // Monitor text node changes
        subtree: true,              // Include all descendant nodes
        attributeOldValue: true,    // Record previous attribute values in mutations[i].oldValue
        characterDataOldValue: false, // Do not record previous text values
        attributeFilter:['style'],  // Only monitor the "style" attribute (CSS changes)
      }
    );

    // Randomly change the position of #target_node every 2 seconds
    tid = setInterval(function(){
        let elm = document.getElementById("target_node");
        let max = 160;
        elm.style.left = Math.floor(Math.random() * max) + 'px';
        elm.style.top  = Math.floor(Math.random() * max) + 'px';
      },
      2000);
  });

  window.addEventListener("beforeunload",function(){
    // Clear the interval
    clearInterval(tid);
    // Stop observing
    observer.disconnect();
  });
</script>