How to fire an event on class change using jQuery?

JavascriptJqueryJquery Events

Javascript Problem Overview


I would like to have something like:

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

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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

Solution 3 - Javascript

Use trigger to fire your own event. When ever you change class add trigger with name

JS Fiddle DEMO

$("#main").on('click', function () {
    $("#chld").addClass("bgcolorRed").trigger("cssFontSet");
});

$('#chld').on('cssFontSet', function () {

    alert("Red bg set ");
});

Solution 4 - Javascript

you can use something like this:

$(this).addClass('someClass');

$(Selector).trigger('ClassChanged')

$(otherSelector).bind('ClassChanged', data, function(){//stuff });

but otherwise, no, there's no predefined function to fire an event when a class changes.

Read more about triggers here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatoeilView Question on Stackoverflow
Solution 1 - JavascriptRory McCrossanView Answer on Stackoverflow
Solution 2 - JavascriptNickolas HookView Answer on Stackoverflow
Solution 3 - JavascriptSatinder singhView Answer on Stackoverflow
Solution 4 - JavascriptDeep SharmaView Answer on Stackoverflow