¿Cómo disparar un evento en el cambio de clase usando jQuery?

Respuestas:

169

No se genera ningún evento cuando cambia una clase. La alternativa es generar manualmente un evento cuando cambia programáticamente la clase:

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

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

ACTUALIZAR

Esta pregunta parece estar reuniendo a algunos visitantes, por lo que aquí hay una actualización con un enfoque que se puede usar sin tener que modificar el código existente usando el nuevo MutationObserver:

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

$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>

Tenga en cuenta que MutationObserversolo está disponible para los navegadores más nuevos, específicamente Chrome 26, FF 14, IE 11, Opera 15 y Safari 6. Consulte MDN para obtener más detalles. Si necesita admitir navegadores heredados, deberá utilizar el método que describí en mi primer ejemplo.

Rory McCrossan
fuente
Al completar esta respuesta, encontré esto: stackoverflow.com/a/1950052/1579667
Benj
1
@Benj Eso es lo mismo que mi respuesta original (es decir, usando trigger()) aunque tenga en cuenta que está muy desactualizado debido al uso de bind(). Sin embargo, no estoy seguro de cómo 'completa' nada
Rory McCrossan
En mi caso, necesitaba disparar un evento cuando un nodo dom tiene una clase específica que MutationObserverfunciona para mí
Mario
Esta es una forma mejorada de esta solución con filter stackoverflow.com/a/42121795/12074818
MVDeveloper1
20

Puede reemplazar las funciones jQuery addClass y removeClass originales con las suyas propias que llamarían a las funciones originales y luego activarían un evento personalizado. (Usando una función anónima autoinvocada para contener la referencia de función original)

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

Entonces, el resto de su código sería tan simple como cabría esperar.

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

Actualizar:

Este enfoque supone que las clases solo se cambiarán a través de los métodos jQuery addClass y removeClass. Si las clases se modifican de otras maneras (como la manipulación directa del atributo de clase a través del elemento DOM) MutationObserver, sería necesario el uso de algo como s como se explica en la respuesta aceptada aquí.

También como un par de mejoras a estos métodos:

  • Desencadena un evento para cada clase que se agrega ( classAdded) o se elimina ( classRemoved) con la clase específica pasada como argumento a la función de devolución de llamada y solo se desencadena si la clase en particular fue realmente agregada (no presente previamente) o eliminada (estuvo presente previamente)
  • Solo se activa classChangedsi se cambia alguna clase

    (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
                    }
                });
                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);
                    }
                });
                prevClasses != this.getAttribute('class') && $this.trigger('classChanged');
            });
            return this;
        }
    })($.fn.removeClass);
    

Con estas funciones de reemplazo, puede manejar cualquier clase cambiada a través de classChanged o clases específicas que se agregan o eliminan al verificar el argumento de la función de devolución de llamada:

$(document).on('classAdded', '#myElement', function(event, className) {
    if(className == "something") { /* do something */ }
});
Gancho Nickolas
fuente
1
Esto funciona bien, pero en el "resto del código" el controlador de eventos debe ser delegada en el selector de destino para permitir la unión de nuevos elementos: $(parent_selector).on('classChanged', target_selector, function(){ /*...*/ });. Me tomó un tiempo ver por qué mis elementos creados dinámicamente no activaban el controlador. Ver learn.jquery.com/events/event-delegation
Timm
2

puedes usar algo como esto:

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

$(Selector).trigger('ClassChanged')

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

pero de lo contrario, no, no hay una función predefinida para disparar un evento cuando cambia una clase.

Lea más sobre los desencadenantes aquí.

Sharma profundo
fuente
1
El controlador del evento debe ser el mismo elemento que desencadena el evento. $ (esto) .addClass ('algunaClase'); $ (Selector) .trigger ('ClassChanged') // Debe ser el mismo Selector $ (Selector) .bind ('ClassChanged', data, function () {// stuff});
Meko Perez Estevez
3
Parece que ha copiado desde aquí, en caso afirmativo, entonces bueno si también proporciona el enlace stackoverflow.com/questions/1950038/…
Satinder singh