Jak odpalić zdarzenie przy zmianie klasy przy użyciu jQuery?

90

Chciałbym mieć coś takiego:

$('#myDiv').bind('class "submission ok" added'){
    alert('class "submission ok" has been added');
});
Matoeil
źródło

Odpowiedzi:

169

W przypadku zmiany klasy nie jest wywoływane żadne zdarzenie. Alternatywą jest ręczne zgłoszenie zdarzenia, gdy programowo zmienisz klasę:

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

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

AKTUALIZACJA

Wydaje się, że to pytanie przyciąga niektórych odwiedzających, więc oto aktualizacja z podejściem, które można zastosować bez konieczności modyfikowania istniejącego kodu za pomocą nowego 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>

Należy pamiętać, że MutationObserverjest dostępna tylko dla nowszych przeglądarek, w szczególności Chrome 26, FF 14, IE 11, Opera 15 i Safari 6. Więcej informacji można znaleźć w MDN . Jeśli potrzebujesz obsługiwać starsze przeglądarki, musisz użyć metody, którą przedstawiłem w moim pierwszym przykładzie.

Rory McCrossan
źródło
Uzupełniając tę ​​odpowiedź, znalazłem to: stackoverflow.com/a/1950052/1579667
Benj
1
@Benj To jest to samo, co moja oryginalna odpowiedź (tj. Używając trigger()), chociaż zauważ, że jest bardzo nieaktualna ze względu na użycie bind(). Nie jestem jednak pewien, jak to „kończy” cokolwiek
Rory McCrossan
W moim przypadku potrzebowałem uruchomić zdarzenie, gdy węzeł dom ma określoną klasę, która MutationObserverdziała dla mnie
Mario
Jest to ulepszona forma tego rozwiązania z filtrem stackoverflow.com/a/42121795/12074818
MVDeveloper1
20

Możesz zastąpić oryginalne funkcje jQuery addClass i removeClass własnymi, które wywoływałyby oryginalne funkcje, a następnie wyzwalały zdarzenie niestandardowe. (Używanie samowywołującej się funkcji anonimowej, aby zawierała odwołanie do oryginalnej funkcji)

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

Wtedy reszta kodu byłaby tak prosta, jak można by się spodziewać.

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

Aktualizacja:

To podejście zakłada, że ​​klasy będą zmieniane tylko za pośrednictwem metod jQuery addClass i removeClass. Jeśli klasy są modyfikowane w inny sposób (np. Bezpośrednia manipulacja atrybutem klasy przez element DOM), MutationObserverkonieczne byłoby użycie czegoś takiego jak s, jak wyjaśniono w zaakceptowanej odpowiedzi tutaj.

Również jako kilka ulepszeń tych metod:

  • Wywołaj zdarzenie dla każdej dodawanej ( classAdded) lub usuwanej ( classRemoved) klasy z określoną klasą przekazaną jako argument do funkcji wywołania zwrotnego i wyzwalane tylko wtedy, gdy dana klasa została faktycznie dodana (wcześniej nieobecna) lub usunięta (była wcześniej obecna)
  • Wyzwalaj tylko classChangedwtedy, gdy jakiekolwiek klasy zostaną faktycznie zmienione

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

Dzięki tym funkcjom zastępczym możesz obsłużyć dowolną klasę zmienioną przez classChanged lub dodać lub usunąć określone klasy, sprawdzając argument funkcji zwrotnej:

$(document).on('classAdded', '#myElement', function(event, className) {
    if(className == "something") { /* do something */ }
});
Nickolas Hook
źródło
1
Działa to dobrze, ale w „reszcie kodu” obsługi zdarzenia powinny być przekazywane do selektora docelowej, aby umożliwić wiązanie się nowe elementy: $(parent_selector).on('classChanged', target_selector, function(){ /*...*/ });. Zajęło mi trochę czasu, aby zobaczyć, dlaczego moje dynamicznie tworzone elementy nie uruchomiły handlera. Zobacz learn.jquery.com/events/event-delegation
Timm,
2

możesz użyć czegoś takiego:

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

$(Selector).trigger('ClassChanged')

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

ale poza tym nie, nie ma predefiniowanej funkcji uruchamiającej zdarzenie w przypadku zmiany klasy.

Przeczytaj więcej o wyzwalaczach tutaj

Deep Sharma
źródło
1
Program obsługi zdarzenia musi być tym samym elementem, który wyzwala zdarzenie. $ (this) .addClass ('someClass'); $ (Selector) .trigger ('ClassChanged') // Musi to być ten sam Selector $ (Selector) .bind ('ClassChanged', data, function () {// stuff});
Meko Perez Estevez,
3
Wygląda na to, że skopiowałeś stąd, jeśli tak, to dobrze, jeśli podasz również link stackoverflow.com/questions/1950038/ ...
Satinder singh