jQuery

Labs jQuery – .sub(), nouveauté jQuery 1.5

Flattr this!

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.

Flattr this!

A propos de Mathieu

Ingénieur développeur web dans la vente par correspondance B2B, adepte de nouvelles technologies et d'innovation. Vous pouvez aussi me retrouver sur Twitter @mathrobin
Cette entrée a été publiée dans jQuery, avec comme mot(s)-clef(s) , , , , , , , , , , , , , , , , . Vous pouvez la mettre en favoris avec ce permalien.
  • supertomate

    Bonjour,

    Su chrome OS X j’ai aussi ce message dans la console :
    Uncaught ReferenceError: $sub is not defined
    (anonymous function)sub:51
    d.d.extend._Deferred.f.resolveWith
    d.d.extend.ready
    d.c.addEventListener.A

  • http://www.mathieurobin.com Mathieu

    Je n’ai pas d’OS X pour tester sous la main, ceci dit, la première ligne d’erreur est « normale ». Etant donné que le script cherche à accéder à un objet ($sub) qui n’existe pas dans la portée courante (comme indiqué par mon commentaire dans le script).
    Pour les 3 lignes suivantes, je suppose que c’est le stacktrace de l’erreur d’après ce que je vois. Je n’ai pas ces infos sur le console Chrome de mon Windows 7, ni sur mon Chromium (8.0.152) sur Ubuntu 10.10.

  • supertomate

    Ah oui Mathieu excuse-moi j’avais pas lu correctement ton commentaire dans le script … donc tout va bien :)
    Sinon oui tu as raison pour les erreurs suivantes

  • https://twitter.com/pomeh Pomeh

    Merci pour l’exemple il est sympa et m’as permis de comprendre le principe de cette méthode. Cela dis, je penses que l’exemple n’est pas vraiment idéal… Regarde ce bout de code

    (function() {
    var $sub = jQuery.sub();
    var privateFunc = function() {
    return ‘hello’;
    };
    })();

    jQuery(function($) {
    $sub(« div »).removeClass(‘rouge’); // provoque une erreur fatale (ReferenceError: $sub is not defined)
    alert( privateFunc() ); // cette ligne provoquera aussi une erreur fatale… mais rien à voir avec jQuery

    console.log( typeof $sub ); // renvoie undefined
    console.log( typeof privateFunc ); // renvoie undefined aussi

    });

    Je penses que tu as compris où je voulais en venir. L’erreur « fatale » dont tu parle n’est pas du tout liée a jQuery, mais elle est liée au fait que $sub est dans déclaré dans la première fonction et qu’on essaye d’y accèder dans la seconde. Les fonctions ne partageant pas le même scope, la variable n’est pas accessible à partir de la deuxième fonction. D’ailleurs le message d’erreur parle de lui-même, il dis bien que « $sub is not defined » et non pas que « .removeClass(‘rouge’) is not defined ». Donc effectivement il y a bien une erreur fatale, mais dans ton exemple cette erreur n’est pas lié à jQuery. Tu pourrais remplacer par exemple la ligne
    $sub(« div »).removeClass(‘rouge’)
    par la ligne
    jQuery(« div »).removeClass(‘rouge’)
    et alors oui effectivement à ce moment là l’erreur provoquée serait en rapport à la fonction sub de jQuery.

    Je sais que dans l’article tu précises bien ceci:
    « 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. »
    mais dans un code de « tous les jours » (code qui s’exécute sur la page, pas le code d’un plugin qui est « inutile » tant que le plugin n’est pas utilisé) les personnes utiliseront jQuery (ou $) plutôt que leur objet $sub. Je penses que la différence est quand même importante pour éviter toute confusion…

  • http://www.mathieurobin.com Mathieu

    Salut,
    Si ils n’utilisent pas leur objet sub, ils n’auront pas l’erreur hors du plugin. ça évite la confusion. Effectivement, c’est bien l’objet et non la méthode qui n’existe pas, comme je l’indique.
    Si ils utilisent jQuery (ou $), ils n’auront pas le souci.

    Je ne suis pas sûr d’avoir saisi ce que tu voulais dire.

Articles liés