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

JavaScriptでDOMのCSSや属性変更を監視する方法|MutationObserverの使い方

JavaScriptでDOMのCSSや属性変更を監視する方法|MutationObserverの使い方

初心者向けに、JavaScriptのMutationObserverを使ってDOMの属性やCSS変更をリアルタイム監視する方法を解説します!
例えば、ページ内の要素のレイアウトが変更された際に、それに合わせて別の要素を動的に調整することが可能です。
MutationObserverを使えば、ページのデザインを適応させる処理を効率的に行えます。
本記事では、コピペで使えるサンプルコード付きで、MutationObserverの基本的な使い方を詳しく説明します。

MutationObserverのメリットとは?

  1. パフォーマンスが良い
    setInterval() で定期的にDOMを監視する方法よりもMutationObserverは変更が発生した瞬間に処理が実行されるため、余計な計算を減らせます。
  2. リアルタイム更新が可能
    CSSが変更されたり、要素が追加・削除されたときに即座に反映できる。
  3. 細かい監視ができる
    属性変更 (attributes)、子要素の追加・削除 (childList)、テキスト変更 (characterData) など、監視範囲を細かく指定可能。

MutationObserverの注意点と使い方

  1. 監視範囲を広げすぎないこと
    subtree: true を使ってページ全体を監視すると不要な変更まで検出しパフォーマンスが低下する可能性があります。
  2. 変更前のデータを記録する場合は注意
    attributeOldValue: true などを設定すると、変更前の値を記録できますが、監視対象が多すぎるとメモリ使用量が増えることに注意。
  3. 適切な解除処理 (disconnect()) を行う
    ページ遷移時や不要になったときに observer.disconnect(); を呼び、不要な監視を避けることが重要。

実行サンプル

<div id="target_node">この要素がランダムに移動</div>
<div id="chase_node">この要素は上記要素を監視して変更を検知したら追従</div>

MutationObserverのコード例|コピペOK

<div style="position:relative;width:100%;height:300px;">
  <div id="target_node" style="position:absolute;width:210px;height:80px;background:#DFF;">
    このノードがランダムに移動
  </div>
  <div id="chase_node"  style="position:absolute;width:210px;height:80px;background:#FFD;">
    この要素は上記要素を監視して変更を検知したら追従
  </div> 
</div>

<script>
  var tid;
  window.addEventListener("load",function(){
    
    //MutationObserverの設定(#target_nodeの変更を監視)
    const observer = new MutationObserver(function(mutations){
      //変更内容は変数mutationsに届く
      //console.log(mutations);

      //#target_node に変更があるので #chase_node を追従させる
      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';
    });
    
    //#target_nodeの監視を開始(スタイル変更を検知)
    observer.observe(
      document.getElementById("target_node"), //監視対象ノードを指定
      {
        childList: true,           //子ノードの追加・削除を監視(テキストノード含む)
        attributes: true,          //要素の属性変更(style, id, class など)を監視
        characterData: true,       //テキストノードの変更も監視(文章が変更された場合など)
        subtree: true,             //子孫ノードも監視対象に含める(階層構造全体を監視可能)
        attributeOldValue: true,   //変更前の属性値を mutations[i].oldValue に記録(以前の値との比較が可能)
        characterDataOldValue: false, //変更前のテキストデータを mutations[i].oldValue に記録(文字列の比較をしたい場合に有効)しない
        attributeFilter:['style'], //監視対象を "style" 属性(つまりCSS)だけに限定
      }
    );

    //2秒ごとに#target_nodeの位置をランダム変更
    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(){
    //インターバルのクリア
    clearInterval(tid);
    //監視を中止
    observer.disconnect();
  });
</script>