Je vous en parlais hier ici, jQuery 1.5 a apporté la possibilité de créer des sous-classes de jQuery lui-même.
Via la méthode magique .sub(), vous pouvez retrouver la documentation officielle ici : http://api.jquery.com/jQuery.sub/
A quoi ça sert?
.sub() permet de créer une instance de jQuery dans laquelle vous pouvez aisément :
- ajouter des méthodes persos,
- modifier des méthodes déjà existantes.
Comment on s'en sert?
Il faut d'abord commencer par importer correctement la librairie jQuery dans sa version 1.5. Pour rappel, si vous essayez avec une version précédente, ça ne fera... bah ça ne fera rien. Tout simplement. J'en profite pour vous présenter le template HTML sur lequel on va travailler.
<script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script> <script type="text/javascript"></script> <!-- .bleu { color : blue; font-weight: bold; } .rouge { color : red; font-weight: bold; } --> <span class="bleu">Test 1</span> <span class="rouge">Test 2</span> <div class="bleu">Test 3</div> <div class="rouge">Test 4</div>
Maintenant qu'on a la structure de HTML et les styles CSS que je veux utiliser ma démo :
Qu'est-ce qu'on va faire?
L'idée de la démo va être d'ajouter une méthode .myCustomMethod() à une instance de sous-classe de jQuery. Cette méthode ne fera que retourner la chaîne 'hello'. On va également modifier le comportement de la fonction .removeClass(). Et pour finir, on va carrément changer la totalité du comportement de la fonction .height() pour qu'elle retourne systématiquement 42.
Mais commençons par créer l'instance de sous-classe de jQuery :
Création de l'instance
(function() { var $sub = jQuery.sub(); $sub(document).ready(function() { }); })(); $(document).ready(function() { });
Il est assez simple de comprendre ce bout de code. Au passage, vous remarquerez que vous disposez désormais de deux corps bien distincts pour exécuter du code lorsque le chargement du document est terminé. Je ne peux que vous suggérer d'utiliser cette séparation pour insérer séparément le code nécessaire aux tests et le code de production. Par exemple 😉
Ajout de la fonction myCustomMethod
Et hop directement la solution, j'explique après :
(function() { var $sub = jQuery.sub(); $sub.fn.myCustomMethod = function(){ return 'hello'; } $sub(document).ready(function() { $sub('body').myCustomMethod(); //= 'hello' }); })(); typeof jQuery('body').myCustomMethod; //= undefined $(document).ready(function() { });
Pour utiliser .myCustomMethod(), il faut l'avoir ajouter à $sub.fn. Si on l'appelle depuis le corps de $sub, la fonction existe et fait son boulot. Mais si vous cherchez à l'utiliser de l'extérieur, tout ce que vous aurez, c'est un crash. Comme le prouve l'utilisation de typeof qui renvoie undefined. Essayez.
Refonte complète de la fonction .height()
Pour rappel, la fonction .height() permet d'obtenir la hauteur d'un élément en pixels. Le but du nouveau code que l'on va définir ici est de simplement retourner 42 :
(function() { var $sub = jQuery.sub(); $sub.fn.myCustomMethod = function(){ return 'hello'; } $.fn.height = function() { return 42; } $sub(document).ready(function() { $sub('body').myCustomMethod(); //= 'hello' $sub('body').height(); //= 42 }); })(); typeof jQuery('body').myCustomMethod; //= undefined $(document).ready(function() { $('body').height(); //= 42 });
J'ai donc ici ajouté la fonction height() à $.fn, pas à $sub.fn, c'est là la subtilité. Que ce soit dans le corps de $sub(document).ready ou $(document).ready, la fonction height() est désormais la même et retourne donc le même résultat. Pratique pour vous permettre de construire des plugins pour jQuery qui iront remplacer des fonctions dont vous n'êtes pas satisfait.
Surcharge de la fonction .removeClass()
Ici le but n'est pas de remplacer totalement le comportement de la fonction .removeClass() de jQuery mais juste d'y apporter quelques modifications, à but de débug par exemple. Nous ajouterons donc un contrôle que la classe CSS que nous voulons supprimer est bien attribuée à la balise cible et nous loguerons dans la console du navigateur quelle classe est retirée de quel élément.
(function() { var $sub = jQuery.sub(); $sub.fn.myCustomMethod = function(){ return 'hello'; } $.fn.height = function() { return 42; } $sub.fn.removeClass = function(classname){ $(this).each(function() { if(classname == this.className) { // on peut utilise la méthode mère, l'original $(this).removeClass(classname); console.log('La classe "' + classname + '" a ete retire de ' + this.localName + '(' + this.textContent + ')'); } }); return this; } $sub(document).ready(function() { $sub('body').myCustomMethod(); //= 'hello' $sub('body').height(); //= 42 $sub("span").removeClass('bleu'); }); })(); typeof jQuery('body').myCustomMethod; //= undefined $(document).ready(function() { $('body').height(); //= 42 $sub("div").removeClass('rouge'); // provoque une erreur fatale });
Il suffit d'ajouter la méthode removeClass() à $sub.fn. Dans le corps, je parcoure tous les éléments sélectionnés, vérifie que le nom de la classe qu'on veut retirer est réellement attribué à l'élément ciblé. Puis on fait appel à la fonction originelle avant de loguer l'action.
Attention, encore une fois, $sub n'existe pas en dehors de son propre corps. Si vous cherchez à appeler $sub.removeClass() depuis l'extérieur de $sub, vous n'obtiendrez qu'une erreur fatale. Autre chose, pour l'exemple, je me suis basé sur le fait que chacun de mes éléments ne possèdent qu'une seule classe CSS, ce qui n'est pas un reflet de la réalité dans la plupart des cas. Ne reproduisez pas ce code sans le corriger, c'est, et restera, un exemple.
Si on exécute ce code, dans la console de Chrome (j'utilise 9.0.597.83 beta) ou de Firefox (j'utilise la 3.6.13), vous obtiendrez ce message : 'La classe "bleu" a ete retire de span(Test 1)'. Pour la console de la beta de IE9 (9.0.7930), vous repasserez. Le reste fonctionne normalement sur les 3 navigateurs.
Bon bah voilà, on a fait le tour, vous pouvez tester le script complet: ici.