Um dos meus truques favoritos da Web foi usar CSS e JavaScript para detectar a inserção e a remoção de um nó do DOM, detalhado em Detectar inserções de nós do DOM com JavaScript e animações de CSS. A técnica e a publicação no blog foram publicadas em uma época em que não tínhamos uma API razoável para detectar esses eventos. Atualmente, temos MutationObserver
, uma API criada para detectar eficientemente cargas de operações de nós. Vamos dar uma olhada!
Básico MutationObserver
API
O MutationObserver
API é um pouco complicada para mim, mas aqui está a configuração básica:
var observer = new MutationObserver(function(mutations) { // For the sake of...observation...let's output the mutation to console to see how this all works mutations.forEach(function(mutation) { console.log(mutation.type); }); }); // Notify me of everything! var observerConfig = { attributes: true, childList: true, characterData: true }; // Node, config // In this case we'll listen to all changes to body and child nodes var targetNode = document.body; observer.observe(targetNode, observerConfig);
Há muitas vantagens em usar o MutationObserver
, mas o resumo é o seguinte:
- Crie uma instância do
MutationObserver
com um retorno de chamada para lidar com qualquer evento lançado em seu caminho - Crie um conjunto de opções para o
MutationObserver
- Ligue para o
observe
do métodoMutationObserver
passando a ele o nó a ser ouvido (…e seus filhos) e a lista de opções. - No momento em que o senhor quiser parar de observar, ligue para
disconnect
MutationObserver
Opções
A MDN fornece detalhes sobre as opções para MutationObserver
:
childList
Definido como true se as adições e remoções dos elementos filhos do nó de destino (incluindo nós de texto) devem ser observadas.attributes
Definido como true se as mutações nos atributos do alvo devem ser observadas.characterData Set
para true se as mutações nos dados do alvo forem observadas.subtree
: Defina como true se as mutações não forem observadas apenas no alvo, mas também nos descendentes do alvo.attributeOldValue
: Defina como true se attributes estiver definido como true e o valor do atributo do alvo antes da mutação precisar ser registrado.characterDataOldValue
: Defina como true se characterData estiver definido como true e os dados do alvo antes da mutação precisarem ser registrados.attributeFilter
: Defina como uma matriz de nomes locais de atributos (sem namespace) se nem todas as mutações de atributos precisarem ser observadas.
É preciso estar ciente de muita coisa ao ouvir um nó e/ou nós filhos!
MutationRecord: MutationObserver
Resultados do manipulador
O objeto resultante quando uma mutação é observada também é detalhado:
type (String)
Retorna atributos se a mutação foi uma mutação de atributo, characterData se foi uma mutação para um nó CharacterData e childList se foi uma mutação para a árvore de nós.target (Node)
Retorna o nó que a mutação afetou, dependendo do tipo. Para atributos, é o elemento cujo atributo foi alterado. Para characterData, é o nó CharacterData. Para childList, é o nó cujos filhos foram alterados.addedNodes (NodeList)
: Retorna os nós adicionados. Será uma NodeList vazia se nenhum nó tiver sido adicionado.removedNodes (NodeList)
: Retorna os nós removidos. Será uma NodeList vazia se nenhum nó tiver sido removido.previousSibling (Node)
Retorna o irmão anterior dos nós adicionados ou removidos, ou nulo.nextSibling (Node)
: Retorna o próximo irmão dos nós adicionados ou removidos, ou nulo.attributeName (String)
: Retorna o nome local do atributo alterado ou nulo.attributeNamespace (String)
Retorna o namespace do atributo alterado, ou nulo.oldValue (String)
: O valor de retorno depende do tipo. Para atributos, é o valor do atributo alterado antes da alteração. Para characterData, são os dados do nó alterado antes da alteração. Para childList, é nulo.
Ufa. Então, vamos dar uma olhada em alguns casos de uso realistas do MutationObserver
.
Detectar quando um nó é inserido
O caso de uso em meu Detectar inserções de nós do DOM com animações JavaScript e CSS O post estava detectando a inserção de nós, portanto, vamos criar um snippet que detecte a inserção de nós:
// Let's add a sample node to see what the MutationRecord looks like // document.body.appendChild(document.createElement('li')); { addedNodes: NodeList[1], // The added node is in this NodeList attributeName: null, attributeNamespace: null, nextSibling: null, oldValue: null, previousSibling: text, removedNodes: NodeList[0], target: body.document, type: "childList" }
O MutationRecord resultante mostra addedNodes: NodeList[1]
o que significa que um nó foi adicionado em um ponto inferior da árvore. O type
é childList
.
Detectar quando um nó é removido
A remoção de um nó mostra o seguinte MutationRecord:
// Now let's explore the MutationRecord when a node is removed // document.body.removeChild(document.querySelector('div')) { addedNodes: NodeList[0], attributeName: null, attributeNamespace: null, nextSibling: text, oldValue: null, previousSibling: null, removedNodes: NodeList[1], // The removed node is in this NodeList target: body.document, type: "childList" }
Esta ação também mostra um type
de childList
mas agora removeNodes
agora tem o NodeList[1]
, a NodeList
com o nó removido.
Detectar alterações de atributos
Se um atributo for alterado em qualquer elemento, o senhor saberá rapidamente; o MutationRecord mostrará:
// What do attribute changes look like? // document.body.setAttribute('id', 'booooody'); { addedNodes: NodeList[0], attributeName: "id", attributeNamespace: null, nextSibling: null, oldValue: null, previousSibling: null, removedNodes: NodeList[0], target: body#booooody.document, type: "attributes" }
Observe também que o target
mostrará o nó para o qual os atributos foram alterados. O oldValue
exibirá seu valor anterior e, enquanto um getAttribute
normal, o senhor verá o novo valor dos atributos.
Pare de ouvir!
Se quiser escrever o aplicativo mais eficiente possível, o senhor só adicionará ouvintes durante o período em que precisar deles e os removerá quando terminar:
observer.disconnect();
O MutationObserver
tem uma instância disconnect
para interromper a escuta. Como seu aplicativo pode ter muitas, muitas operações DOM, talvez o senhor queira ter a capacidade de desconectar o ouvinte durante o tempo em que o usuário interage com a página.
O MutationObserver
API parece um pouco verborrágica, mas é poderosa, informativa e, em última análise, livre de hack. O brilhante “hack” original de Daniel Buchner oferece melhor suporte para adição e remoção de nós, mas o MutationObserver
provavelmente deve ser usado, se possível.