MutationObserver в JavaScript

В этом руководстве вы узнаете, как использовать JavaScript MutationObserver API для отслеживания изменений, вносимых в дерево DOM.

API MutationObserver позволяет отслеживать изменения, вносимые в дерево DOM. Когда узлы DOM изменяются, вы можете вызвать функцию обратного вызова, чтобы отреагировать на изменения.

Основные шаги для использования MutationObserver API:

  • Во-первых, определите функцию обратного вызова, которая будет выполняться при изменении DOM:
function callback(mutations) {
    // 
}
  • Во-вторых, создайте объект MutationObserver и передайте обратный вызов конструктору MutationObserver() :
let observer = new MutationObserver(callback);
  • В-третьих, вызовите метод observe(), чтобы начать наблюдение за изменениями DOM.
observer.observe(targetNode, observerOptions);

Метод observe() имеет два параметра. target является корнем поддерева узлов для отслеживания изменений. Параметр ObserverOptions содержит свойства, которые указывают, какие изменения DOM должны передаваться в обратный вызов observerOptions.

Наконец, закончите наблюдать за изменениями DOM, вызвав метод disconnect() :

observer.disconnect();

Опции

Второй аргумент observe() позволяет указать параметры для описания MutationObserver :

let options = {
    childList: true,
    attributes: true,
    characterData: false,
    subtree: false,
    attributeFilter: ['attr1', 'attr2'],
    attributeOldValue: false,
    characterDataOldValue: false
};

Вам не нужно использовать все варианты. Однако, чтобы MutationObserver работал, по крайней мере один из childList, attributes или characterData должен быть установлен в true, иначе методObserver observer() выдаст ошибку.

Наблюдение за изменениями в дочерних элементах

Предположим, что у вас есть следующий список:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MutationObserver Demo: ChildList</title>
</head>
<body>
    <ul id="language">
        <li>HTML</li>
        <li>CSS</li>
        <li>JavaScript</li>
        <li>TypeScript</li>
    </ul>

    <button id="btnStart">Start Observing</button>
    <button id="btnStop">Stop Observing</button>
    <button id="btnAdd">Add</button>
    <button id="btnRemove">Remove the Last Child</button>

    <script src="app.js"></script>
</body>
</html>

В следующем примере показано, как использовать свойство childList объекта options мутации для отслеживания изменений дочернего узла.

  • Сначала выберите такие элементы, как list и buttons, используя метод querySelector(). По умолчанию кнопка « Stop Observing » disabled.
// selecting list
let list = document.querySelector('#language');

// selecting buttons
let btnAdd = document.querySelector('#btnAdd');
let btnRemove = document.querySelector('#btnRemove');
let btnStart = document.querySelector('#btnStart');

let btnStop = document.querySelector('#btnStop');
btnStop.disabled = true;
  • Во-вторых, объявите функцию log(), которая будет использоваться в качестве обратного вызова для MutationObserver :
function log(mutations) {
    for(let mutation of mutations) {
        if(mutation.type === 'childList') {
            console.log(mutation);
        }
    }
}
  • В-третьих, создайте новый объект:
let observer = new MutationObserver(log);
  • В-четвертых, начните наблюдать за изменениями DOM в дочерних узлах элемента списка, когда нажата кнопка « Start Observing », вызвав метод observe() с childList объекта options, установленным в значение true :
btnStart.addEventListener('click', function() {
    observer.observe(list, {
        childList: true
    });
    
    btnStart.disabled = true;
    btnStop.disabled = false;
});
  • В-пятых, добавьте новый элемент списка при нажатии кнопки add :
let counter = 1;
btnAdd.addEventListener('click', function() {
    // create a new item element
    let item = document.createElement('li');
    item.textContent = `Item ${counter++}`;

    // append it to the child nodes of list
    list.appendChild(item);
});
  • В-шестых, удалите последний дочерний элемент list при нажатии кнопки « Remove »:
btnRemove.addEventListener('click', function() {
    list.lastElementChild ?
        list.removeChild(list.lastElementChild) :
        console.log('No more child node to remove');
});
  • Наконец, прекратите наблюдение за изменениями DOM при нажатии кнопки « Stop Observing », вызвав метод disconnect() объекта MutationObserver :
btnStop.addEventListener('click', function() {
    observer.disconnect();    
    // set button states
    btnStart.disabled = false;
    btnStop.disabled = true;
});

Соединим все это вместе:

(function() {
    // selecting the list
    let list = document.querySelector('#language');

    // selecting the buttons
    let btnAdd = document.querySelector('#btnAdd');
    let btnRemove = document.querySelector('#btnRemove');
    let btnStart = document.querySelector('#btnStart');

    // disable the stop button
    let btnStop = document.querySelector('#btnStop');
    btnStop.disabled = true;

    function log(mutations) {
        for(let mutation of mutations) {
            if(mutation.type === 'childList') {
                console.log(mutation);
            }
        }
    }

    let observer = new MutationObserver(log);

    btnStart.addEventListener('click', function() {
        observer.observe(list, {
            childList: true
        });

        btnStart.disabled = true;
        btnStop.disabled = false;
    });

    btnStop.addEventListener('click', function() {
        observer.disconnect();

        // Set the button state
        btnStart.disabled = false;
        btnStop.disabled = true;
    });

    let counter = 1;
    btnAdd.addEventListener('click', function() {
        // create a new item element
        let item = document.createElement('li');
        item.textContent = `Item ${counter++}`;

        // append it to the child nodes of list
        list.appendChild(item);
    });

    btnRemove.addEventListener('click', function() {
        list.lastElementChild ?
            list.removeChild(list.lastElementChild) :
            console.log('No more child node to remove');
    });

})();

Обратите внимание, что мы поместили весь код в IIFE (выражение немедленно вызываемой функции).

Наблюдение за изменениями атрибутов

Чтобы наблюдать за изменениями атрибутов, вы используете следующее свойство attributes объекта options :

let options = {
  attributes: true
}

Если вы хотите наблюдать за изменениями одного или нескольких определенных attributes, игнорируя другие, вы можете использовать свойство attributeFilter :

let options = {
  attributes: true,
  attributeFilter: ['class', 'style']
}

В этом примере MutationObserver будет вызывать обратный вызов каждый раз, когда изменяется атрибут class или style.

Изменения в поддереве

Чтобы отслеживать целевой узел и его поддерево узлов, вы устанавливаете для свойства subtree объекта options значение true :

let options = {
    subtree: true
}

character data

Чтобы отслеживать изменения текстового содержимого узла, вы устанавливаете для свойства characterData объекта options значение true :

let options = {
    characterData: true
}

Доступ к старым значениям

Чтобы получить доступ к старым значениям атрибутов, вы устанавливаете для свойства attributeOldValue объекта options значение true :

let options = {
    attributes: true,
    attributeOldValue: true
}

Точно так же вы можете получить доступ к старому значению символьных данных, установив для свойства characterDataOldValue объекта options значение true :

let options = {
    characterData: true,
    subtree: true,
    characterDataOldValue: true
}

В этом руководстве вы узнали об API JavaScript MutationObserver, который отслеживает изменения DOM и выполняет обратный вызов каждый раз, когда происходит изменение.

Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript