How to fire an event on class change using jQuery?

I would like to have something like:

$('#myDiv').bind('class "submission ok" added'){
    alert('class "submission ok" has been added');
});

There is no event raised when a class changes. The alternative is to manually raise an event when you programatically change the class:

$someElement.on('event', function() {
    $('#myDiv').addClass('submission-ok').trigger('classChange');
});

// in another js file, far, far away
$('#myDiv').on('classChange', function() {
     // do stuff
});

UPDATE

This question seems to be gathering some visitors, so here is an update with an approach which can be used without having to modify existing code using the new MutationObserver:

var $div = $("#foo");
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    var attributeValue = $(mutation.target).prop(mutation.attributeName);
    console.log("Class attribute changed to:", attributeValue);
  });
});

observer.observe($div[0], {
  attributes: true,
  attributeFilter: ['class']
});

$div.addClass('red');
.red {
  color: #C00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo" class="bar">#foo.bar</div>

Be aware that the MutationObserver is only available for newer browsers, specifically Chrome 26, FF 14, IE 11, Opera 15 and Safari 6. See MDN for more details. If you need to support legacy browsers then you will need to use the method I outlined in my first example.


You could replace the original jQuery addClass and removeClass functions with your own that would call the original functions and then trigger a custom event. (Using a self-invoking anonymous function to contain the original function reference)

(function( func ) {
    $.fn.addClass = function() { // replace the existing function on $.fn
        func.apply( this, arguments ); // invoke the original function
        this.trigger('classChanged'); // trigger the custom event
        return this; // retain jQuery chainability
    }
})($.fn.addClass); // pass the original function as an argument

(function( func ) {
    $.fn.removeClass = function() {
        func.apply( this, arguments );
        this.trigger('classChanged');
        return this;
    }
})($.fn.removeClass);

Then the rest of your code would be as simple as you'd expect.

$(selector).on('classChanged', function(){ /*...*/ });

Update:

JS Fiddle Demo

This approach does make the assumption that the classes will only be changed via the jQuery addClass and removeClass methods. If classes are modified in other ways (such as direct manipulation of the class attribute through the DOM element) use of something like MutationObservers as explained in the accepted answer here would be necessary.

Also as a couple improvements to these methods:

  • Trigger an event for each class being added (classAdded) or removed (classRemoved) with the specific class passed as an argument to the callback function and only triggered if the particular class was actually added (not present previously) or removed (was present previously)

  • Only trigger classChanged if any classes are actually changed

      (function( func ) {
          $.fn.addClass = function(n) { // replace the existing function on $.fn
              this.each(function(i) { // for each element in the collection
                  var $this = $(this); // 'this' is DOM element in this context
                  var prevClasses = this.getAttribute('class'); // note its original classes
                  var classNames = $.isFunction(n) ? n(i, prevClasses) : n.toString(); // retain function-type argument support
                  $.each(classNames.split(/\s+/), function(index, className) { // allow for multiple classes being added
                      if( !$this.hasClass(className) ) { // only when the class is not already present
                          func.call( $this, className ); // invoke the original function to add the class
                          $this.trigger('classAdded', className); // trigger a classAdded event
                      }
                  });
                  if( prevClasses != this.getAttribute('class') ) $this.trigger('classChanged'); // trigger the classChanged event
              });
              return this; // retain jQuery chainability
          }
      })($.fn.addClass); // pass the original function as an argument
    
      (function( func ) {
          $.fn.removeClass = function(n) {
              this.each(function(i) {
                  var $this = $(this);
                  var prevClasses = this.getAttribute('class');
                  var classNames = $.isFunction(n) ? n(i, prevClasses) : n.toString();
                  $.each(classNames.split(/\s+/), function(index, className) {
                      if( $this.hasClass(className) ) {
                          func.call( $this, className );
                          $this.trigger('classRemoved', className);
                      }
                  });
                  if( prevClasses != this.getAttribute('class') ) $this.trigger('classChanged');
              });
              return this;
          }
      })($.fn.removeClass);
    

With these replacement functions you can then handle any class changed via classChanged or specific classes being added or removed by checking the argument to the callback function:

$(document).on('classAdded', '#myElement', function(event, className) {
    if(className == "something") { /* do something */ }
});