Home > Back-end >  How to observe an the body element for changes in its class attribute in native javascript?
How to observe an the body element for changes in its class attribute in native javascript?

Time:01-12

I wanna be able to observe the body element for changes in its class attribute.

I found this code online:

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});

And I didn't really understand what's going on with the prevClassState. Thanks in advance.

CodePudding user response:

MutationObserver itself detects mutations but in your case it does not really tell whether specific class added or removed.

In your example the logic is simple. Before you attach mutation listener to the element you find out if the element already has such class name. After that when mutation happens (either added or removed) you again follow the same logic and check if the class name is there or not. If the element had a value and after mutation not there - its clear that class has been added. To keep this approach working you have to reset the first variable by checking class name again and assign the state to it.

I thought it would be more convenient to write implementation for it.

You have classObserver function with three parameters: the element you observing, observable class name and a callback function which provides you state of the class alongside mutation object and observer.

Also, there is an Object prototype and you can use to chain it with the element itself.

Check out the examples below.

/**
* @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" >Click me</button>
<button id="btn-2" >Click me - proto</button>

CodePudding user response:

`

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>

`I would suggest implementing a method to subscribe to the class change from where the class being updated.

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)
}
  •  Tags:  
  • Related