Javascript - assistindo mudanças no DOM sem temporizadores
Existem basicamente duas formas de ouvir mudanças em um elemento no DOM: ou você usa eventos, ou, em casos mais extremos, temporizadores.
Como saber quando um atributo de um elemento mudou? Como saber se os filhos de um elemento foram alterados? Não tem outra forma de fazer, que não seja usando temporizadores?
Sim, tem! E é isso que eu quero mostrar nesse post! Vem comigo :D
Quando uma ação é executada em algum elemento do DOM (click, mouseenter, mousedown, etc.), é possível atribuir um evento a esse elemento, que irá disparar uma função de callback para que você possa manipular o elemento no momento certo.
Mas, e quando temos, por exemplo, uma mudança de atributo? Ou ainda, uma mudança na árvore do DOM, onde você precisa saber exatamente quando esses eventos ocorrem para tomar alguma decisão? Como fazer?
Provavelmente você logo pensa:
Temporizadores! Vou usar um setInterval()
ou um setTimeout()
recursivo, e, a cada x milissegundos, eu executo uma função :D
Funciona, mas não é a melhor solução. Se você tiver muitos temporizadores executando juntos, você pode acabar deixando sua aplicação lenta, e até travar o navegador do usuário!
Como eu posso então resolver isso de forma elegante?
MutationObserver
MutationObserver nos dá um caminho para reagir à mudanças no DOM. Ele foi projetado para substituir o Mutation Events, que foi definido na especificação de Eventos do DOM 3.
O MUtationObserver
é um construtor, e funciona de forma parecida com o addEventListener
, com a diferença que ele não é atribuído mais de uma vez por elemento, se for utilizada a mesma instância.
Com o addEventListener
, se você atribuir duas vezes um listener para o evento de click
, ao clicar nesse elemento, a função de callback será disparada duas vezes. Isso não acontece com o MutationObserver
. Independente de quantas vezes você atribuí-lo ao mesmo elemento, o callback executará só uma vez.
Construtor
Para começarmos a observar as mudanças em um elemento, primeiro precisamos instanciar o construtor:
1 | var observer = new MutationObserver( callback ); |
A função de callback é executada a cada mudança no DOM, e ela pode receber dois argumentos: o primeiro é um array de objetos, cada um do tipo MutationRecord. O segundo é uma instância do MutationObserer
.
Métodos da instância
observe()
O observe()
registra a instância do MutationObserver
para receber notificações do DOM para um nó específico:
1 | observer.observe( target, options ); |
Parâmetro target:
O target
é o nó que será observado por mudanças.
O options
é um objeto que especifíca as mudanças que devem ser observadas.
Por exemplo:
Se eu quiser verificar quando um nó descendente (filho) for adicionado ou removido, ou quando um atributo for adicionado / alterado ou removido, eu posso usar da seguinte maneira:
1 | <div class="mydiv" data-js="div"></div> |
1 | var target = document.querySelector( '[data-js="div"]' ); |
No meu HTML, adicionei uma div com um atributo data-js="div"
e uma classe mydiv
.
No JS, atribuímos esse nó para a variável target
.
A variável observer
será nosso observador. Então instanciamos o MutationObserver
nessa varíável, passando como parâmetro a função de callback handleMutationObserver
.
Depois criamos mais uma variável que receberá as configurações do que deve ser observado no elemento.childList
verifica mudanças nos filhos do nó correspondente. Se algum nó for adicionado ou removido do nó principal (target
), será disparada a função de callback. Isso é válido também para nós de texto, não precisa ser somente tags.
attributes
observa os atributos do elemento. Se algo mudar, o callback também é disparado.
Para ver todos os parâmetros que você pode utilizar, consulte esse link.
Faça o teste, jogando o código acima no seu console! :D
Só troque o target
para um elemento válido :)
Se você não quiser mais observar um elemento, pode usar o método disconnect()
:
1 | observer.disconnect(); |
Compatibilidade de browsers
Por ser algo relativamente novo, o MutationObserver
só funciona em browsers modernos:
- Chrome - 18+;
- Firefox - 14+;
- IE - 11+;
- Opera - 15+;
- Safari - 6+;
Polyfill
Para funcionar em todos os browsers, você pode usar um polyfill, feito com temporizadores. Não é o mais recomendado, mas funciona. O polyfill você encontra nesse link.
É possível fazer muita coisa com o MutationObserver. Recomendo que você dê uma lida na documentação completa dele aqui: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Até a próxima! :D
Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/
Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1