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');
});
Matoeil
fonte

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.

Rory McCrossan
fonte
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 */ }
});
Nickolas Hook
fonte
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

Deep Sharma
fonte
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