Observador de mutação JavaScript

Resumo : neste tutorial, você aprenderá como usar a MutationObserverAPI JavaScript para observar as alterações feitas na árvore DOM.

Introdução à API JavaScript MutationObserver

A MutationObserverAPI permite monitorar as alterações feitas na árvore DOM. Quando os nós DOM mudam, você pode invocar uma função de retorno de chamada para reagir às mudanças.

As etapas básicas para usar a MutationObserverAPI são:

Primeiro, defina a função de retorno de chamada que será executada quando o DOM mudar:

function callback(mutations) {
    // 
}Linguagem de código:  JavaScript  ( javascript )

Segundo, crie um MutationObserverobjeto e passe o retorno de chamada para o MutationObserver()construtor:

let observer = new MutationObserver(callback);Linguagem de código:  JavaScript  ( javascript )

Terceiro, chame o observe()método para começar a observar as alterações do DOM.

observer.observe(targetNode, observerOptions);Linguagem de código:  JavaScript  ( javascript )

O observe()método possui dois parâmetros. A targeté a raiz da subárvore de nós para monitorar alterações. O observerOptionsparâmetro contém propriedades que especificam quais alterações no DOM devem ser relatadas ao retorno de chamada do observador.

Por fim, pare de observar as alterações do DOM chamando o disconnect()método:

observer.disconnect();Linguagem de código:  JavaScript  ( javascript )

As opções do MutationObserver

O segundo argumento do observe()método permite especificar opções para descrever MutationObserver:

let options = {
    childList: true,
    attributes: true,
    characterData: false,
    subtree: false,
    attributeFilter: ['attr1', 'attr2'],
    attributeOldValue: false,
    characterDataOldValue: false
};Linguagem de código:  JavaScript  ( javascript )

Você não precisa usar todas as opções. No entanto, para funcionar MutationObserver, pelo menos um de childList, attributesou characterDataprecisa ser definido como true, caso contrário, o observer()método gerará um erro.

Observando mudanças em elementos filhos

Supondo que você tenha a seguinte lista:

<!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>
Linguagem de código:  HTML, XML  ( xml )

O exemplo a seguir ilustra como usar a childListpropriedade do optionsobjeto de mutação para monitorar as alterações do nó filho.

Primeiro, selecione os elementos como liste buttonsusando o querySelector()método. Por padrão, o Stop Observingbotão é 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;Linguagem de código:  JavaScript  ( javascript )

Segundo, declare uma log()função que será usada como retorno de chamada para MutationObserver:

function log(mutations) {
    for (let mutation of mutations) {
        if (mutation.type === 'childList') {
            console.log(mutation);
        }
    }
}Linguagem de código:  JavaScript  ( javascript )

Terceiro, crie um novo MutationObserverobjeto:

let observer = new MutationObserver(log);
Linguagem de código:  JavaScript  ( javascript )

Quarto, comece a observar as alterações do DOM nos nós filhos do elemento da lista quando o Start Observingbotão é clicado, chamando o observe()método com o childListdo optionsobjeto definido como true:

btnStart.addEventListener('click', function () {
    observer.observe(list, {
        childList: true
    });
    
    btnStart.disabled = true;
    btnStop.disabled = false;
});
Linguagem de código:  JavaScript  ( javascript )

Quinto, adicione um novo item de lista quando o addbotão for clicado:

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);
});
Linguagem de código:  JavaScript  ( javascript )

Sexto, remova o último filho de listquando o Removebotão for clicado:

btnRemove.addEventListener('click', function () {
    list.lastElementChild ?
        list.removeChild(list.lastElementChild) :
        console.log('No more child node to remove');
});
Linguagem de código:  JavaScript  ( javascript )

Por fim, pare de observar as alterações do DOM quando o Stop Observingbotão é clicado, chamando o disconnect()método do MutationObserverobjeto:

btnStop.addEventListener('click', function () {
    observer.disconnect();    
    // set button states
    btnStart.disabled = false;
    btnStop.disabled = true;
});Linguagem de código:  JavaScript  ( javascript )

Junte tudo:

(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');
    });

})();
Linguagem de código:  JavaScript  ( javascript )

Observe que colocamos todo o código em um IIFE (Immediately Invoked Function Expression).

Observando mudanças nos atributos

Para observar alterações nos atributos, você usa a seguinte attributespropriedade do optionsobjeto:

let options = {
  attributes: true
}
Linguagem de código:  JavaScript  ( javascript )

Se quiser observar as alterações em um ou mais específicos attributesenquanto ignora os outros, você pode usar a attributeFilterpropriedade:

let options = {
  attributes: true,
  attributeFilter: ['class', 'style']
}Linguagem de código:  JavaScript  ( javascript )

Neste exemplo, o MutationObserverirá invocar o retorno de chamada sempre que o atributo classou stylefor alterado.

Observando mudanças em uma subárvore

Para monitorar o nó de destino e sua subárvore de nós, defina a subtreepropriedade do optionsobjeto como true:

let options = {
    subtree: true
}
Linguagem de código:  JavaScript  ( javascript )

Observando alterações nos dados do personagem

Para monitorar o nó em busca de alterações em seu conteúdo textual, defina a characterDatapropriedade do optionsobjeto como true:

let options = {
    characterData: true
}Linguagem de código:  JavaScript  ( javascript )

Acessando valores antigos

Para acessar os valores antigos dos atributos, você define a attributeOldValuepropriedade do optionsobjeto como true:

let options = {
    attributes: true,
    attributeOldValue: true
}Linguagem de código:  JavaScript  ( javascript )

Da mesma forma, você pode acessar o valor antigo dos dados do caractere definindo a characterDataOldValuepropriedade do optionsobjeto como true:

let options = {
    characterData: true,
    subtree: true,
    characterDataOldValue: true
}Linguagem de código:  JavaScript  ( javascript )

Um exemplo prático de MutationObserver

Em aplicativos JavaScript, os elementos da página normalmente são gerados dinamicamente. Para esperar por um elemento dinâmico, você precisa usar MutationObserver.

A waitForElement()função a seguir espera por um ou mais elementos especificados por um seletor usando MutationObserver.

function waitForElement(selector) {
  return new Promise((resolve) => {
    if (document.querySelector(selector)) {
      return resolve(element);
    }
    const observer = new MutationObserver(() => {
      const element = document.querySelector(selector);
      if (element) {
        resolve(element);
        observer.disconnect();
      }
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });
  });
}Linguagem de código:  JavaScript  ( javascript )

Como funciona.

A waitForElement()função retorna uma promessa. A promessa será resolvida assim que o elemento estiver disponível.

Primeiro, resolva o elemento se estiver disponível:

if (document.querySelector(selector)) {
    return resolve(element);
}Linguagem de código:  JavaScript  ( javascript )

Segundo, crie um novo MutationObserverobjeto para observar a árvore DOM se o elemento não estiver disponível:

const observer = new MutationObserver(() => {
  const element = document.querySelector(selector);
  if (element) {
    resolve(element);
    observer.disconnect();
  }
});Linguagem de código:  JavaScript  ( javascript )

O objeto observador chamará a função resolve() assim que o elemento estiver disponível e parará de observar a árvore DOM.

Terceiro, observe os elementos de toda a árvore DOM:

observer.observe(document.body, {
      childList: true,
      subtree: true,
});Linguagem de código:  CSS  ( css )

Como waitForElement()retorna a Promise, você pode usar o then()método assim:

waitForElement()('.a-class').then((element) => {
    console.log('Element is ready');
    console.log(element.textContent);
});Linguagem de código:  JavaScript  ( javascript )

Ou você pode usar awaita sintaxe:

const element = await waitForElement()('.a-class');
console.log(element.textContent);Linguagem de código:  JavaScript  ( javascript )

Neste tutorial, você aprendeu sobre a MutationObserverAPI JavaScript que monitora as alterações do DOM e executa um retorno de chamada sempre que a alteração ocorre.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *