Quiero poder observar el elemento del cuerpo en busca de cambios en su atributo de clase.
Encontré este código en línea:
var elemToObserve = document.getElementById('your_elem_id'); var prevClassState = elemToObserve.classList.contains('your_class'); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if(mutation.attributeName == "class"){ var currentClassState = mutation.target.classList.contains('your_class'); if(prevClassState !== currentClassState) { prevClassState = currentClassState; if(currentClassState) console.log("class added!"); else console.log("class removed!"); } } }); }); observer.observe(elemToObserve, {attributes: true});Y realmente no entendí qué estaba pasando con el prevClassState. Gracias por adelantado.
MutationObserver en sí mismo detecta mutaciones, pero en su caso realmente no dice si se agregó o eliminó una clase específica.
En tu ejemplo, la lógica es simple. Antes de adjuntar el detector de mutación al elemento, averigüe si el elemento ya tiene ese nombre de clase. Después de eso, cuando ocurre la mutación (ya sea agregada o eliminada), vuelve a seguir la misma lógica y verifica si el nombre de la clase está allí o no. Si el elemento tenía un valor y después de la mutación no está allí, está claro que se ha agregado la clase. Para mantener este enfoque funcionando, debe restablecer la primera variable verificando el nombre de la clase nuevamente y asignarle el estado.
Pensé que sería más conveniente escribir una implementación para ello.
Tiene la función classObserver con tres parámetros: el elemento que observa, el nombre de la clase observable y una función de devolución de llamada que le proporciona el estado de la clase junto con el objeto de mutación y el observador.
Además, hay un prototipo de Object y puede usarlo para encadenarlo con el elemento en sí.
Echa un vistazo a los ejemplos a continuación.
/** * @param {Element} targetElement * @param {String} observableClassName * @param {Function} callback */ function classObserver(targetElement, observableClassName, callback){ const ref = {prevStateHasClass: targetElement.classList.contains(observableClassName)}; return new MutationObserver(function(mutations, observer) { // Use traditional 'for loops' for IE 11 for(const mutation of mutations) { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const currentStateHasClass = mutation.target.classList.contains(observableClassName); if(ref.prevStateHasClass !== currentStateHasClass){ ref.prevStateHasClass = currentStateHasClass; const classAdded = currentStateHasClass; callback.call(classAdded, classAdded, mutation, observer); } } } }).observe(targetElement, { attributes: true }); }; const button1 = document.getElementById('btn-1'); // for demo, mutate classList button1.onclick = () => button1.classList.toggle('my-class-observe'); // usage classObserver(button1, 'my-class-observe', function(classAdded, mutation, observer){ // You canalso use `this` instead of `classAdded` console.log('Change detected. Class ', classAdded? 'added': 'removed'); // console.log(classAdded, mutation, observer); // do disconnect if needed // observer.disconnect(); }); Object.prototype.observeClassMutation = function(observableClassName, callback){ return classObserver(this, observableClassName, callback); }; const button2 = document.getElementById('btn-2'); // Just for demo button2.onclick = () => button2.classList.toggle('target-class-2-observe'); // usage button2.observeClassMutation('target-class-2-observe', function(classAdded, mutation, observer){ console.log('Change detected. Class ', classAdded? 'added': 'removed'); }); <button id="btn-1" class="one two">Click me</button> <button id="btn-2" class="one two target-class-2-observe">Click me - proto</button>`
class UpdateClass { constructor(){ this.callbacks=[]; } UpdateClass() { document.querySelector('body').classList.add('new-class'); document.querySelector('button.red').addEventListener('click', () => { this.callbacks.forEach(callback => callback('red')) }); document.querySelector('button.green').addEventListener('click', () => { this.callbacks.forEach(callback => callback('green')) }); this.callbacks.forEach(callback => callback('new-class')) } subscribeToClassChange(callback){ this.callbacks.push(callback); } } // start of module where you want to observe the change const updateClassObj = new UpdateClass(); updateClassObj.subscribeToClassChange(handleClassChange); updateClassObj.UpdateClass(); function handleClassChange(className) { // runs every time there is a class change console.log(className) } .red { color: red; } .green { color: green } <body> <button class='red'>update class to red</button> <button class='green'>update class to green</button> </body>`Sugeriría implementar un método para suscribirse al cambio de clase desde donde se actualiza la clase.
class UpdateClass { constructor(){ const callbacks=[]; } UpdateClass() { document.querySelector('body').classList.add('new-class'); this.callbacks.forEach(callback => callback('new-class')) } subscribeToClassChange(callback){ this.callbacks.push(callback); } } // start of module where you want to observe the change const updateClassObj = new UpdateClass(); updateClassObj.subscribe(handleClassChange)); updateClassObj.UpdateClass(); function handleClassChange(className) { // runs every time there is a class change console.log(className) }