【javascript】MutationObserver でDOM要素を監視しよう!

javascript
この記事は約7分で読めます。

はじめに

最近では javascript では、できないことがないんじゃないかといったくらいたくさんのことができるようになってきましたね。個人的には、最近 javascript 好きでいろいろと触ってみています!

javascript ってホント初学者のときは、DOMを操作できてajax通信できるものなんだなぁくらいの認識しかなく、結構ないがしろにしていたのが悔まれます。。。いまからでも遅くない!一つ一つキャッチアップしていこうと息巻いている今日この頃です!

クロたん
クロたん

千里の道も一歩からだにゃ~。

クロたん
クロたん

Never Too Late だにゃ~。

わい
わい

よーーーしゃべんなぁ。

というわけで、今回はクライアントサイドの機能を紹介できればと思います!

結論(全体像)

HTML

    <div id="js-wrapper">
        <!-- ここに要素が追加される -->
    </div>

    <!-- ボタン群 -->
    <button id="js-append" type="button">
        要素追加
    </button>

    <button id="js-delete" type="button">
        要素削除
    </button>

    <button id="js-change-attr" type="button">
        属性追加
    </button>

    <button id="js-observe-start" type="button">
        監視再開
    </button>

    <button id="js-observe-end" type="button">
        監視終了
    </button>

javascript

document.addEventListener('DOMContentLoaded', () => {
    // 要素を追加していくwrapper要素を取得し、この要素を監視していく
    const $parent = document.getElementById('js-wrapper')

    // 要素追加ボタンがクリックされたら要素を追加
    document.getElementById('js-append').addEventListener('click', () => {
        let html = '<p>';
        html += '追加要素';
        html += '</p>';
        $parent.innerHTML += html
    });

    // 要素削除ボタンがクリックされたら要素を削除
    document.getElementById('js-delete').addEventListener('click', () => {
        if ($parent.children.length === 0) return false;
        $parent.lastChild.remove();
    });

    // 属性追加ボタンがクリックされたら属性を追加
    document.getElementById('js-change-attr').addEventListener('click', () => {
        $parent.setAttribute('class', 'test')
    });

    // 監視再開ボタンがクリックされた監視再開
    document.getElementById('js-observe-start').addEventListener('click', () => {
        observer.observe($parent, mutation_configs);
    });

    // 監視終了ボタンがクリックされた監視終了
    document.getElementById('js-observe-end').addEventListener('click', () => {
        observer.disconnect();
    });

    // MutationObserverインスタンスを生成する
    const observer = new MutationObserver(function (mutations) {
        mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
                console.log('子要素が追加か削除されたよ!')
            } else if (mutation.type === 'attributes') {
                console.log('属性が追加されたよ!')
            }
        })
    })

    // 監視したい変更を定義する
    const mutation_configs = {
        childList: true,// $parentの子要素を監視
        attributes: true,// $parentの属性を監視
        // subtree: true, ← childListは子までだが、subtreeは子孫要素も含む。より広範囲を対象とする
        // characterData: true,← $parentのデータに対する変更を監視
    };
    // 監視を開始する
    observer.observe($parent, mutation_configs)
});

解説!

ボタン群に関してはそれぞれイベントハンドラーをそれぞれ定義しているって感じなので、HTMLとjsの該当箇所に関しては解説は省略します!

ポイントとなるのは以下!

    // MutationObserverインスタンスを生成する
    const observer = new MutationObserver(function (mutations) {
        mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
                console.log('子要素が追加か削除されたよ!')
            } else if (mutation.type === 'attributes') {
                console.log('属性が追加されたよ!')
            }
        })
    })

    // 監視したい変更を定義する
    const mutation_configs = {
        childList: true,// $parentの子要素を監視
        attributes: true,// $parentの属性を監視
        // subtree: true, ← childListは子までだが、subtreeは子孫要素も含む。より広範囲を対象とする
        // characterData: true,← $parentのデータに対する変更を監視
    };
    // 監視を開始する
    observer.observe($parent, mutation_configs)
ケン
ケン

もはや、定型文で覚えてもいいくらいのものかもしれないですね。。。

インスタンスを生成して、監視する対象をconfigで定義して、監視を開始する といった流れです。
解説という見出しをつけましたが、ほとんどコメントに書いてある以上・以下でもないことに気づきましたw

さいごに

自分の場合は、tagglejs といったタグを wordpress っぽく設定できるjsライブラリを使った際に、どうしても長いタグのときにフォームの高さを超えてタグがフォーム外に溢れてしまうといった現象に遭遇したため、今回紹介した MutationObserver を使いました。

正直なところ、あまり可読性も高くなく、コードも煩雑になってしまうので避けたかったのですが、自分の見つけた最善の策でした。。。


紹介しておいてなんですが、、、
あまり、ユースケースがない気もしますので最終手段的な感じで使うのがいいかなと思います!

クロ
クロ

要件次第ってやっちゃなぁ~~~。

コメント