[embedyt] https://www.youtube.com/watch?v=Mq8Pp8Dqy5Y[/embedyt]
De seguro, que alguna vez haz escuchado hablar sobre el MutationObserver, o las mutaciones del DOM en JavaScript.
Cada día las peticiones de nuestros clientes o las necesidades de nuestras aplicaciones son más exigentes.
Con lo cual debemos estar a la altura de dichos requerimientos.
Esta Web API, nos ayuda a detectar cambios en el DOM.
Podremos saber cuando se añade o elimina un elemento en el DOM, si se modifican atributos de los elementos, si hay cambios en el textContent de dichos nodos.
O incluso si hay cambios en los hijos de los elementos.
MutationObserver es una nueva API de los navegadores modernos, y viene a reemplazar al antiguo metodo MutationEvents, el cual ya está obsoleto.
El metodo MutationObserver
Este constructor crea y retorna un nuevo observer el cual invoca una función de callback, cuando ocurre algún evento en el DOM (document object model).
Como usar MutationObserver
La implementación del MutationObserver en tu app es realmente facil. Necesitamos crear una nueva instancia del MutationObserver, pasandole una función como callback, que se invocará cada vez que se produzca una mutación.
El primer argumento que nos devolverá, es una colección de todas las mutaciones, que hayan ocurrido.
Cada mutación proporciona información sobre su tipo y los cambios que se han producido.
Sintaxis:
const observer = new MutationObserver(callback);
callback
Es una función que será llamada cada vez que ocurra un cambio en el DOM.
Logicamente dichos cambios, serán observados en el elemento o sub-elementos, que nosotros decidamos.
Un MutationObserver de tipo object, será devuelto, cuando haya cambios en el DOM.
// configuration of the observer:
const observerOptions = { attributes: true, childList: true, characterData: true };
observer.observe(equipos, observerOptions);
Si deseamos no continuar observando los cambios, podremos deterner el MutationObserver con el metodo disconnect.
observer.disconnect();
https://giphy.com/gifs/cchq-alan-tudyk-lets-do-this-con-man-3o72F03RnbPTvKtR7y
No más teoria y vamos ver un ejemplo de como detectar cambios en el DOM
Para realizar este ejemplo, necesitaremos crear tres ficheros, [index.html, app.js y api.js].
Vamos a simular que recibimos data desde una api (api.js), con un listado de equipos de futbol.
Y de manera random, elegimos un equipo del array.
[index.html]
En el fichero index.html, tendremos una estructura básica html. El elemento destacado, en este fichero es el el ul con la clase equipos.
[api.js]
const equiposFutbol_array = ['Betis', 'Espanyol', 'Valencia', 'Sevilla'];
setTimeout(function addTeam() {
const equipos = document.querySelector(‘ul.equipos’);
const equipo = document.createElement(‘li’);
equipo.textContent = equiposFutbol_array[parseInt(Math.random() * equiposFutbol_array.length, 10)];
equipos.appendChild(equipo);
}, Math.random() * 1000)
En el fichero api.js creamos un array con cuatro elementos, luego en la función addTeam
en la linea 1 de la función seleccionamos el ul con la clase equipos, que serán nuestro target.
luego creamos un nuevo elemento del tipo li, dentro del ul y le insertamos el contenido aleatorio del array.
[app.js]
const observer = new MutationObserver((mutationList) => {
mutationList.forEach((mutation)=> {
if(mutation.addedNodes.length){
console.log('Añadido', mutation.addedNodes[0]);
}
if(mutation.removedNodes.length){
console.log('Eliminado', mutation.removedNodes[0]);
}
console.log(mutation.type);
})
});
const equipos = document.querySelector('ul.equipos') // elemento que queremos observar
// Opcions para el observer
const observerOptions = {
attributes: true,
childList: true,
subtree: true,
characterData: false,
attributeOldValue: false,
characterDataOldValue: false
};
observer.observe(equipos, observerOptions);
Y finalmente en el app.js, es donde vamos a utilizar el MutationObserver, en el primer bloque instanciamos el observer, y le pasamos como parametro una función callback, que nos devolverá mutationList, luego iteramos con un forEach, cada mutation.
Podemos saber especificamente, el tipo de mutation, el target afectado, si ha añadido elementos (addedNodes) o si hemos eliminado elementos (removedNodes).
Listado de todas las propiedades del MutationRecord
Propiedad | Tipo | Descripción |
tipo |
String |
Returns attributes if the mutation was an attribute mutation, characterData if it was a mutation to a CharacterData node, and childList if it was a mutation to the tree of nodes. |
target |
|
Returns the node the mutation affected, depending on the tipo . For attributes , it is the element whose attribute changed. For characterData , it is the CharacterData node. For childList , it is the node whose children changed. |
addedNodes |
|
Return the nodes added. Will be an empty NodeList if no nodes were added. |
removedNodes |
|
Return the nodes removed. Will be an empty NodeList if no nodes were removed. |
previousSibling |
|
Return the previous sibling of the added or removed nodes, or null . |
nextSibling |
|
Return the next sibling of the added or removed nodes, or null . |
attributeName |
String |
Returns the local name of the changed attribute, or null . |
attributeNamespace |
String |
Returns the namespace of the changed attribute, or null . |
oldValue |
String |
The return value depends on the type . For attributes , it is the value of the changed attribute before the change. For characterData , it is the data of the changed node before the change. For childList , it is null . |
Lo siguiente es la declaración de la constante equipos, que es el elemento, que queremos observar.
El otro bloque con la constante observerOptions, es donde definimos las opciones y es realmente una parte primordial de nuestro código.
En este caso estamos indicando al observer, que observe los atributos del elementos class, style, etc.
También con la propiedad childList, si está establecida a true, nos informará en caso si se ha añadido o elimina elementos de nuestro target, incluido los nodos text.
Otra propiedad interesante es subtree, establecida a true, no solo nos informa si hay existido mutación en nuestro target, sino que observa tambien los hijos de nuestro target.
El resto de propiedades “seteadas” a false, son menos utilizadas, y no vamos a profundizar en esta guia, sobre su uso.
const observerOptions = {
attributes: true,
childList: true,
subtree: true,
characterData: false,
attributeOldValue: false,
characterDataOldValue: false
};
Luego comenzamos a observar los nodos especificado en la constante equipos, y como segundo parametro le pasamos las opcioines.
observer.observe(equipos, observerOptions);
Una vez, unidas todas las piezas de nuestra aplicación, podemo ir al index.html, y podemos ver en la consola como nos informa que se ha añadido un elemento, incluso vemos el elemento en la consola.
Esto es gracias a la propiedad childList, pero como he te comentado antes, también podemos trabajar con atributos.
Vamos a añadir dos nuevas lineas de codigo, en la función addTeam, del fichero api.js
equipo.classList.add("newClass");
equipo.title = "newAlternateText";
Quedando el código de esta forma.
setTimeout(function addTeam() {
const equipos = document.querySelector('ul.equipos');
const equipo = document.createElement('li');
equipo.textContent = equiposFutbol_array[parseInt(Math.random() * equiposFutbol_array.length, 10)];
equipos.appendChild(equipo);
equipo.classList.add("newClass");
equipo.title = "newAlternateText";
}, Math.random() * 2000)
La primera va a añadir una clase, llamada newClass al elemento li, en la segunda añadimos el atributo title a dicho elemento.
Debes tener en cuenta, que los atributos irán en funcion del elemento con el que estemos trabajando; Por ejemplo con un elemnto img, podríamos trabajar con los atributos alt y src.
Ahora dentro del forEach, en el fichero app.js, podemo añadir un console, y ver el tipo de mutación que se ha llevado a cabo.
Vamos a utilizar función count del console, y de esta forma podemos ver la salida de texto en lineas separadas.
console.count(mutation.type);
Quedando el código de esta forma.
const observer = new MutationObserver((mutationList) => { // mutations : este paramentro nos devuelve todos los cambios
mutationList.forEach((mutation)=> {
if(mutation.addedNodes.length){
console.log('Añadido', mutation.addedNodes[0]);
}
if(mutation.removedNodes.length){
console.log('Eliminado', mutation.removedNodes[0]);
}
console.count(mutation.type);
})
});
Vamos a añadir otro metodo removeTeam en el fichero api.js, tan solo para que puedas ver con un ejemplo sencillo y practico la otra funcion del childList.
setTimeout(function removeTeam() {
const equipos = document.querySelector('ul.equipos');
equipos.removeChild(equipos.querySelector('li'));
}, Math.random() * 2000 + 2000)
Ahora nuestro fichero api.js, quedará así.
const equiposFutbol_array = ['Betis', 'Espanyol', 'Valencia', 'Sevilla'];
setTimeout(function addTeam() {
const equipos = document.querySelector('ul.equipos');
const equipo = document.createElement('li');
equipo.textContent = equiposFutbol_array[parseInt(Math.random() * equiposFutbol_array.length, 10)];
equipos.appendChild(equipo);
equipo.classList.add("newClass");
equipo.title = "newAlternateText";
}, Math.random() * 1000)
setTimeout(function removeTeam() {
const equipos = document.querySelector('ul.equipos');
equipos.removeChild(equipos.querySelector('li'));
}, Math.random() * 2000 + 2000)
Volvemos al index.html, actualizamos y podemos comprobar como ahora nos indica que el elemento fué eliminado, y la propiedad encargada de esta mutación childList
Hasta aquí esta breve introducción, sobre el metodo MutationObserver.
Recuerda que tienes el código el github y también si quieres ver en youtube esta intro, te dejo el link.
Hasta la próxima..
Recuerda compartir es vivir.
https://giphy.com/gifs/rupaulsdragrace-episode-11-rupauls-drag-race-62d3HL71aENrUQQpEO