You can use the API MutationObserver , define a callback that processes the changes and Then you are told what to observe next to the list of things to observe.
In this example, I'll look at the list of children's nodes, their attributes and changes in the children's tree.
Most of the code shown is to simulate an ajax with promises and variable times, in addition to implementing a console log that does not cover the result.
In addition to adding items and deleting items, there are 2 extra buttons that control the observer to start observing and not observing.
In each item there are 2 more buttons that change an attribute or delete the item.
The observer passes to the callback (each time it detects a change) a list of "mutations", in this list each mutation has a type, and a target or a list of target nodes, the wave is to traverse the mutations and verify the type.
// creamos el observador
const panoptico = new MutationObserver(
(mutaciones, observer) => {
for (var m of mutaciones) {
if (m.type == 'childList') {
consolog('[espia] Cambió el número de items');
} else if (m.type == 'attributes') {
consolog(
'[espia] El atributo ',
m.attributeName,
' de ',
m.target.querySelector('h2').innerHTML,
'fue modificado.');
}
}
}
);
var $dinamico = document.getElementById("dinamico");
// comenzar a observar
document.getElementById("espia").onclick = function() {
consolog('te estoy viendo');
panoptico.observe(
$dinamico, {
attributes: true,
childList: true,
subtree: true
},
);
}
// dejar de observar
document.getElementById("desespia").onclick = function() {
consolog('ya no se que haces');
panoptico.disconnect();
}
// resto del code ejemplo
// para que el log no tape las cosas
var consolog = function(...cosas) {
var logs = document.getElementById('logs');
cosas.forEach((t) => {
logs.innerHTML += t + " ";
});
logs.innerHTML += "\n";
logs.scrollTop = logs.scrollHeight;
};
let descripciones = ["arbol", "pera", "batata", "semaforo"];
var start = Date.now();
// timeout random por usar el wifi del hotel
var fetchDescripcion = function(id) {
return new Promise((resolve, reject) => {
consolog('fetching', id);
window.setTimeout(() => {
resolve({
id: id % descripciones.length,
descripcion: descripciones[id % descripciones.length],
});
}, (Math.random() * 4 + 2) * 1000);
});
}
var setDescripcion = function(d) {
consolog('resolve', d.id, d.descripcion);
var info = document.createElement('div');
info.className = "info";
var nombre = document.createElement('h2');
nombre.innerHTML = "(" + d.id + ") " + d.descripcion;
var masinfo = document.createElement('p');
masinfo.innerHTML = "Marca de tiempo: " + (Date.now() - start);
var marcaMe = document.createElement('button');
marcaMe.classList.add("btn", "btn-marcar");
marcaMe.innerHTML = "⍟";
marcaMe.onclick = function(ev) {
var elem = ev.target.parentNode;
elem.classList.toggle('marcado');
}
var borraMe = document.createElement('button');
borraMe.classList.add("btn", "btn-borrar");
borraMe.innerHTML = "⃠";
borraMe.onclick = function(ev) {
var elem = ev.target.parentNode;
elem.parentNode.removeChild(elem);
}
info.appendChild(nombre);
info.appendChild(marcaMe);
info.appendChild(borraMe);
info.appendChild(masinfo);
$dinamico.appendChild(info);
}
var getDescripcion = function(id) {
fetchDescripcion(id).then(x => setDescripcion(x));
}
document.getElementById("pone").onclick = function() {
for (var i = 0; i < 5; i++) {
var dId = Math.floor(Math.random() * 20);
getDescripcion(dId);
}
}
document.getElementById("saca").onclick = function() {
while ($dinamico.lastChild) {
$dinamico.removeChild($dinamico.lastChild);
}
start = Date.now();
}
#info {
position: fixed;
right: 0;
top: 25px;
bottom: 0
}
#logs {
height: 160px;
overflow-y: scroll;
min-width: 50vw;
background: #fafafa;
border: 1px solid #ccc;
}
.btn {
border: 1px solid #ccc;
border-radius: 5px;
width: 25px;
height: 25px;
line-height: 20px;
font-size: 14px;
text-align: center;
overflow: hidden;
background: #fff;
display: inline-block;
padding: 2px;
margin: 2px;
cursor: pointer;
}
.btn-marcar {
background: #fe0;
color: #000;
}
.btn-borrar {
background: #f00;
color: #fff;
}
.marcado {
background: #fed;
}
.info {
border: 1px dashed #000;
border-radius: 5px;
padding: 5px;
margin: 5px 0;
}
h2,
p {
padding: 0;
margin: 0 0 5px 0;
}
<html>
<body>
<nav>
<button id="pone">agregar 5</button>
<button id="saca">limpiar</button>
<button id="espia">empezar a observar</button>
<button id="desespia">dejar de observar</button>
</nav>
<section id="info">
<PRE id="logs"></PRE>
</section>
<section id="dinamico">
<div>
</div>
</section>
</body>
</html>