Como disparar um evento na mudança de classe usando jQuery?


91

Eu gostaria de ter algo como:

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

Respostas:


172

Nenhum evento é gerado quando uma classe muda. A alternativa é gerar manualmente um evento ao alterar programaticamente a classe:

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

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

ATUALIZAR

Esta questão parece estar atraindo alguns visitantes, então aqui está uma atualização com uma abordagem que pode ser usada sem a necessidade de modificar o código existente usando o novo 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>

Esteja ciente de que o MutationObserverestá disponível apenas para navegadores mais recentes, especificamente Chrome 26, FF 14, IE 11, Opera 15 e Safari 6. Consulte MDN para obter mais detalhes. Se você precisar oferecer suporte a navegadores legados, precisará usar o método que descrevi no meu primeiro exemplo.


Ao completar esta resposta, encontrei o seguinte: stackoverflow.com/a/1950052/1579667
— Benj

1
@Benj Essa é a mesma resposta original (ou seja, usando trigger()), embora observe que ela está muito desatualizada devido ao uso de bind(). Não tenho certeza de como isso 'completa' alguma coisa, no entanto
— Rory McCrossan

No meu caso, eu precisava disparar um evento quando um nó dom tem uma classe específica que MutationObserverfunciona para mim
— Mario

1
Esta é uma forma aprimorada desta solução com o filtro stackoverflow.com/a/42121795/12074818
— MVDeveloper1

20

Você poderia substituir as funções addClass e removeClass originais do jQuery pelas suas próprias, que chamariam as funções originais e, em seguida, disparariam um evento personalizado. (Usando uma função anônima de auto-invocação para conter a referência da função 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);

Então, o resto do seu código seria tão simples quanto você esperava.

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

Atualizar:

Essa abordagem pressupõe que as classes só serão alteradas por meio dos métodos jQuery addClass e removeClass. Se as classes forem modificadas de outras maneiras (como a manipulação direta do atributo de classe por meio do elemento DOM), use algo comoMutationObserver s, conforme explicado na resposta aceita aqui, seria necessário.

Além disso, algumas melhorias nesses métodos:

  • Acione um evento para cada classe sendo adicionada ( classAdded) ou removida (classRemoved ) com a classe específica passada como um argumento para a função de retorno de chamada e apenas acionado se a classe específica foi realmente adicionada (não presente anteriormente) ou removida (estava presente anteriormente)
  • Só acionar classChangedse alguma classe for realmente alterada

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

Com essas funções de substituição, você pode manipular qualquer classe alterada via classChanged ou classes específicas adicionadas ou removidas, verificando o argumento da função de retorno de chamada:

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

1
Esta multa funciona, mas no "resto do código" o manipulador de eventos deve ser delegado o seletor alvo para permitir a ligação a novos elementos: $(parent_selector).on('classChanged', target_selector, function(){ /*...*/ });. Levei um tempo para ver por que meus elementos criados dinamicamente não acionaram o manipulador. Consulte learn.jquery.com/events/event-delegation
— Timm


2

você pode usar algo assim:

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

$(Selector).trigger('ClassChanged')

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

mas caso contrário, não, não há função predefinida para disparar um evento quando uma classe muda.

Leia mais sobre gatilhos aqui


1
O manipulador do evento deve ser o mesmo item que acionou o evento. $ (this) .addClass ('someClass'); $ (Selector) .trigger ('ClassChanged') // Deve ser o mesmo Seletor $ (Selector) .bind ('ClassChanged', data, function () {// stuff});
— Meko Perez Estevez

3
Parece que você copiou daqui, se sim, então ótimo se você fornecer o link também stackoverflow.com/questions/1950038/…
— Satinder singh
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.