246 votes

Déclenchement d'événements sur les changements de classe CSS en jQuery

Comment puis-je déclencher un événement si une classe CSS est ajoutée ou modifiée à l'aide de jQuery ? La modification d'une classe CSS déclenche-t-elle l'événement jQuery ? change() événement ?

8 votes

Sept ans plus tard, avec l'adoption assez large des MutationObservers dans les navigateurs modernes, la réponse acceptée ici devrait vraiment être mise à jour pour être M. Br's .

0 votes

Cela pourrait vous aider. stackoverflow.com/questions/2157963/

0 votes

En complément de la réponse de Jason, j'ai trouvé ceci : stackoverflow.com/a/19401707/1579667

228voto

Jason Points 20255

Chaque fois que vous changez une classe dans votre script, vous pourriez utiliser un trigger pour créer votre propre événement.

$(this).addClass('someClass');
$(mySelector).trigger('cssClassChanged')
....
$(otherSelector).bind('cssClassChanged', data, function(){ do stuff });

mais sinon, non, il n'y a pas de moyen intégré de déclencher un événement lorsqu'une classe change. change() se déclenche uniquement lorsque le focus quitte une entrée dont l'entrée a été modifiée.

$(function() {
  var button = $('.clickme')
      , box = $('.box')
  ;

  button.on('click', function() { 
    box.removeClass('box');
    $(document).trigger('buttonClick');
  });

  $(document).on('buttonClick', function() {
    box.text('Clicked!');
  });
});

.box { background-color: red; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="box">Hi</div>
<button class="clickme">Click me</button>

Plus d'informations sur les déclencheurs jQuery

4 votes

Quel est l'intérêt de déclencher l'événement qui appelle ensuite une fonction ? Pourquoi ne pas appeler la fonction directement, au lieu de la déclencher ?

1 votes

Il semble un peu étrange que le PO veuille écouter un événement 'cssClassChanged' ; la classe a sûrement été ajoutée à la suite d'un autre événement d'application tel que 'colourChanged', ce qui aurait plus de sens pour l'annoncer avec un trigger. Je ne peux pas imaginer pourquoi quelque chose serait intéressé par un changement de nom de classe, c'est plutôt le résultat du classNameChange.

0 votes

Si c'était spécifiquement un changement de className qui était intéressant, alors j'imagine que décorer jQuery.fn.addClass pour déclencher 'cssClassChanged' serait plus raisonnable comme @micred l'a dit.

143voto

Arash Milani Points 2142

À mon avis, la meilleure solution consiste à combiner les deux réponses de @RamboNo5 et @Jason.

Je veux dire remplacer la fonction addClass et ajouter un évènement personnalisé appelé cssClassChanged

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // trigger a custom event
        jQuery(this).trigger('cssClassChanged');

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    $("#YourExampleElementID").bind('cssClassChanged', function(){ 
        //do stuff here
    });
});

0 votes

Cela semble en effet être la meilleure approche, mais bien que je lie l'événement uniquement à "#YourExampleElementID", il semble que tous les changements de classe (c'est-à-dire sur n'importe quel élément de ma page) soient traités par ce gestionnaire... une idée ?

0 votes

Cela fonctionne bien pour moi @FilipeCorreia . Pouvez-vous s'il vous plaît fournir un jsfiddle avec la reproduction de votre cas ?

0 votes

@Goldentoa11 Vous devriez prendre du temps un soir ou un week-end et surveiller tout ce qui se passe sur une pageload typique qui utilise beaucoup la publicité. Vous serez époustouflé par le volume de scripts qui tentent (parfois sans succès de la manière la plus spectaculaire) de faire des choses comme ça. J'ai rencontré des pages qui déclenchent des dizaines d'événements DOMNodeInserted/DOMSubtreeModified par seconde, totalisant des centaines d'événements au moment où vous arrivez à $() quand l'action réelle commence.

64voto

Mr Br Points 213

Si vous voulez détecter un changement de classe, le meilleur moyen est d'utiliser des observateurs de mutation, qui vous donnent un contrôle total sur tout changement d'attribut. Cependant, vous devez définir vous-même l'écouteur et l'ajouter à l'élément que vous écoutez. L'avantage est que vous n'avez pas besoin de déclencher quoi que ce soit manuellement une fois que l'écouteur est ajouté.

$(function() {
(function($) {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

    $.fn.attrchange = function(callback) {
        if (MutationObserver) {
            var options = {
                subtree: false,
                attributes: true
            };

            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(e) {
                    callback.call(e.target, e.attributeName);
                });
            });

            return this.each(function() {
                observer.observe(this, options);
            });

        }
    }
})(jQuery);

//Now you need to append event listener
$('body *').attrchange(function(attrName) {

    if(attrName=='class'){
            alert('class changed');
    }else if(attrName=='id'){
            alert('id changed');
    }else{
        //OTHER ATTR CHANGED
    }

});
});

Dans cet exemple, l'écouteur d'événements est ajouté à chaque élément, mais ce n'est pas ce que vous voulez dans la plupart des cas (pour économiser de la mémoire). Ajoutez cet écouteur "attrchange" à l'élément que vous voulez observer.

1 votes

Cette solution semble faire exactement la même chose que la réponse acceptée, mais avec plus de code, sans être prise en charge par certains navigateurs et avec moins de flexibilité. le seul avantage qu'elle semble avoir est qu'elle déclenchera à chaque fois quelque chose change sur la page, ce qui va absolument détruire vos performances...

10 votes

Votre déclaration dans la réponse est incorrecte @Json, c'est en fait la façon de faire si vous ne voulez pas activer un déclencheur manuellement, mais l'activer automatiquement. Ce n'est pas seulement un avantage, c'est l'avantage, car cela a été demandé dans la question. Deuxièmement, c'était juste un exemple, et comme il est dit dans l'exemple, il n'est pas suggéré d'ajouter un écouteur d'événements à chaque élément, mais seulement aux éléments que vous voulez écouter, donc je ne comprends pas pourquoi cela va détruire les performances. Et enfin, c'est l'avenir, la plupart des derniers navigateurs le supportent, caniuse.com/#feat=mutationobserver S'il vous plaît, reconsidérez l'édition, c'est la bonne voie.

1 votes

Le site insertionQ bibliothèque vous permet d'attraper les nœuds DOM qui apparaissent s'ils correspondent à un sélecteur. Il a un support plus large des navigateurs parce qu'il n'utilise pas DOMMutationObserver .

53voto

RamboNo5 Points 1154

Je vous suggère de remplacer la fonction addClass. Vous pouvez le faire de cette façon :

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // call your function
        // this gets called everytime you use the addClass method
        myfunction();

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    // do stuff
});

16 votes

Je pense que la meilleure approche serait de combiner cette méthode avec la méthode $(mySelector).trigger('cssClassChanged') de Jason.

4 votes

Il existe un plugin qui fait cela de manière générique : github.com/aheckmann/jquery.hook/blob/master/jquery.hook.js

37 votes

C'est un bon moyen d'embrouiller un futur mainteneur.

22voto

yckart Points 7517

Juste une preuve de concept :

Regardez le gist pour voir des annotations et rester à jour :

https://gist.github.com/yckart/c893d7db0f49b1ea4dfb

(function ($) {
  var methods = ['addClass', 'toggleClass', 'removeClass'];

  $.each(methods, function (index, method) {
    var originalMethod = $.fn[method];

    $.fn[method] = function () {
      var oldClass = this[0].className;
      var result = originalMethod.apply(this, arguments);
      var newClass = this[0].className;

      this.trigger(method, [oldClass, newClass]);

      return result;
    };
  });
}(window.jQuery || window.Zepto));

L'utilisation est assez simple, il suffit d'ajouter un nouveau listender sur le nœud que vous voulez observer et de manipuler les classes comme d'habitude :

var $node = $('div')

// listen to class-manipulation
.on('addClass toggleClass removeClass', function (e, oldClass, newClass) {
  console.log('Changed from %s to %s due %s', oldClass, newClass, e.type);
})

// make some changes
.addClass('foo')
.removeClass('foo')
.toggleClass('foo');

0 votes

Cela a fonctionné pour moi, sauf que je ne pouvais pas lire l'ancienne ou la nouvelle classe avec la méthode removeClass.

1 votes

@slashdottir Pouvez-vous créer un violon et expliquer ce que exactement ne fonctionne pas .

0 votes

Oui... ça ne marche pas. TypeError : this[0] is undefined in "return this[0].className ;"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X