Comment déclencher un événement lors d'un changement de classe à l'aide de jQuery?

90

J'aimerais avoir quelque chose comme:

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

Réponses:

169

Aucun événement n'est déclenché lorsqu'une classe change. L'alternative consiste à déclencher manuellement un événement lorsque vous modifiez la classe par programme:

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

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

MISE À JOUR

Cette question semble rassembler quelques visiteurs, voici donc une mise à jour avec une approche qui peut être utilisée sans avoir à modifier le code existant en utilisant le nouveau 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>

Sachez que le MutationObservern'est disponible que pour les navigateurs plus récents, en particulier Chrome 26, FF 14, IE 11, Opera 15 et Safari 6. Voir MDN pour plus de détails. Si vous devez prendre en charge les navigateurs hérités, vous devrez utiliser la méthode que j'ai décrite dans mon premier exemple.

Rory McCrossan
la source
En complétant cette réponse, j'ai trouvé ceci: stackoverflow.com/a/1950052/1579667
Benj
1
@Benj C'est la même chose que ma réponse initiale (c'est-à-dire en utilisant trigger()) bien que notez qu'elle est très obsolète en raison de l'utilisation de bind(). Je ne sais pas comment cela `` complète '' quoi que ce soit
Rory McCrossan
Dans mon cas, j'avais besoin de déclencher un événement lorsqu'un nœud dom a une classe spécifique MutationObserverfonctionne pour moi
Mario
Il s'agit d'une forme améliorée de cette solution avec filter stackoverflow.com/a/42121795/12074818
MVDeveloper1
20

Vous pouvez remplacer les fonctions jQuery addClass et removeClass d'origine par les vôtres qui appelleraient les fonctions d'origine, puis déclencheraient un événement personnalisé. (Utilisation d'une fonction anonyme auto-appelante pour contenir la référence de fonction d'origine)

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

Ensuite, le reste de votre code serait aussi simple que prévu.

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

Mise à jour:

Cette approche suppose que les classes ne seront modifiées que via les méthodes jQuery addClass et removeClass. Si les classes sont modifiées par d'autres moyens (comme la manipulation directe de l'attribut class via l'élément DOM), l'utilisation de quelque chose comme MutationObservers comme expliqué dans la réponse acceptée ici serait nécessaire.

Aussi comme quelques améliorations à ces méthodes:

  • Déclenche un événement pour chaque classe ajoutée ( classAdded) ou supprimée ( classRemoved) avec la classe spécifique passée en argument à la fonction de rappel et déclenchée uniquement si la classe particulière a été réellement ajoutée (non présente précédemment) ou supprimée (était présente précédemment)
  • Ne se déclenche que classChangedsi des classes sont réellement modifiées

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

Avec ces fonctions de remplacement, vous pouvez ensuite gérer toute classe modifiée via classChanged ou des classes spécifiques ajoutées ou supprimées en vérifiant l'argument de la fonction de rappel:

$(document).on('classAdded', '#myElement', function(event, className) {
    if(className == "something") { /* do something */ }
});
Nickolas Hook
la source
1
Cela fonctionne très bien, mais dans le « reste du code » le gestionnaire d'événements doit être délégué au sélecteur cible pour permettre la liaison aux nouveaux éléments: $(parent_selector).on('classChanged', target_selector, function(){ /*...*/ });. Il m'a fallu un certain temps pour voir pourquoi mes éléments créés dynamiquement n'ont pas déclenché le gestionnaire. Voir learn.jquery.com/events/event-delegation
Timm
2

vous pouvez utiliser quelque chose comme ceci:

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

$(Selector).trigger('ClassChanged')

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

mais sinon, non, il n'y a pas de fonction prédéfinie pour déclencher un événement lorsqu'une classe change.

En savoir plus sur les déclencheurs ici

Sharma profond
la source
1
Le gestionnaire de l'événement doit être le même élément qui déclenche l'événement. $ (this) .addClass ('someClass'); $ (Selector) .trigger ('ClassChanged') // Ce doit être le même Selector $ (Selector) .bind ('ClassChanged', data, function () {// stuff});
Meko Perez Estevez
3
On dirait que vous avez copié à partir d'ici, si oui, bien si vous fournissez un lien trop stackoverflow.com/questions/1950038/...
Satinder singh