I. Prérequis

Cet article est basé sur l'acquis après la lecture de l'article : « Adapter jQuery à vos besoins » et de ses prérequis.

De plus, le lecteur doit avoir une bonne maîtrise du JavaScript et de la bibliothèque jQuery, car il ne trouvera aucune aide sur les techniques réputées acquises dans cet article.

II. Introduction

On peut trouver sur le Web de nombreuses méthodes pour écrire un plugin multitâche.

Le guide officiel résume l'essentiel des bonnes pratiques, mais il ne s'agit que d'un point de départ. Il promeut l'usine de construction de widget UI, mais plusieurs auteurs trouvent cette méthode lourde et contraignante.

Plusieurs méthodes de construction utilisent une technique particulière pour résoudre un aspect particulier. Certains auteurs préfèrent mettre l'accent sur l'écriture de modules, sur l'utilisation de prototypes, sur l'utilisation des événements, etc.

Il n'existe pas un modèle idéal applicable en toutes circonstances et la méthode utilisée peut avoir une grande influence sur les performances, sur la lisibilité et sur la maintenabilité du code de votre plugin.

Addy Osmani a recensé et étudié les approches principales, j'ai résumé ci-dessous les principales qualités recherchées par les auteurs de méthodes de plugin.

La méthode de construction doit :
  1. S'intégrer dans un espace de noms ou permettre l'usage d'un espace de noms ;
  2. Cacher la « plomberie » et « automatiser » les tâches répétitives ;
  3. Placer la logique du plugin dans un objet séparé du « système de construction » du plugin pour améliorer la lisibilité et la maintenabilité ;
  4. Permettre la gestion de propriétés, d'objets, et de méthodes publiques et privées ;
  5. Offrir une méthode versatile pour l'initialisation de l'instance (un objet jQuery) ;
  6. Empêcher l'initialisation multiple ;
  7. Offrir une méthode pour la destruction de l'instance ;
  8. Sauvegarder l'état (les options) de l'instance ;
  9. Être capable de gérer et de déclencher des événements, ce qui permet une utilisation asynchrone par un système de souscription et de publication d'événements ;
  10. Permettre la définition et la modification des options par défaut ;
  11. Permettre la définition d'options par l'intermédiaire d'un attribut data HTML5. Ce qui permet l'initialisation d'une collection d'instances avec les options par défaut, suivi d'une personnalisation par instance.

La méthode que j'ai mise au point respecte ce cahier des charges.

Ce n'est sans doute pas « La Méthode » (Addy Osmani conclut qu'il n'y en a pas) mais elle a deux qualités essentielles :
  • elle a résisté à toutes les épreuves que je lui ai fait subir pendant plus de trois mois ;
  • elle me convient bien.

Certes, le dernier argument n'est pas rationnel, mais à mes yeux c'est le principal et il est incontestable ! ;)

J'ai bien entendu des choses à reprocher aux autres méthodes, mais en dehors du fait que la plupart ne satisfont pas à mon cahier des charges, les autres arguments sont essentiellement subjectifs.

Pour tester les codes, veuillez charger le ZIP.

III. La méthode de construction

Le plugin multitâche appelle une fonction « universelle » qui gère l'appel aux méthodes d'un objet, que j'appelle simplement un objet méthodes, c'est lui qui contient l'intégralité du « savoir-faire » du plugin.

Par fonction « universelle », il faut comprendre que c'est la même fonction qui gère tous les dvjhPlugins.

Rappel : les fenêtres de code s'ouvrent en cliquant sur le bouton placé à droite.

III-A. La structure d'un plugin

Dans l'espace de nom dvjh (Adapter jQuery à vos besoins) on écrira :

Espace de nom
CacherSélectionnez
  1. var dvjh = ( function( $ ){ 
  2.   
  3.     // objet méthodes du plugin A 
  4.     var dvjhPluginAMethods = { 
  5.     }; 
  6.   
  7.     // objet méthodes du plugin B 
  8.     var dvjhPluginBMethods = { 
  9.     }; 
  10.   
  11.     /* 
  12.      * Ajouts de méthodes (plugin) au prototype de l'objet jQuery ($.fn). 
  13.      * 
  14.      * Pour créer un dvjhPlugin il suffit de lui donner 
  15.      * un nom et d'appeler la fonction dvjhPluginCreate dans le 
  16.      * contexte du plugin. 
  17.      * Les paramètres sont le nom de son objet méthodes et les 
  18.      * arguments de l'utilisateur. 
  19.      */ 
  20.     $.extend( $.fn, { 
  21.         "dvjhPluginA" : function(){ 
  22.             return dvjhPluginCreate.call( this, dvjhPluginAMethods, arguments ); 
  23.         }, 
  24.         "dvjhPluginB" : function(){ 
  25.             return dvjhPluginCreate.call( this, dvjhPluginBMethods, arguments ); 
  26.         } 
  27.     }); 
  28.   
  29.     return { 
  30.         "$" : $ 
  31.     }; 
  32.   
  33. })( jQuery.sub() ); 

III-B. La fonction dvjhPluginCreate

Cette fonction gère l'initialisation du plugin (avec ou sans options utilisateur, avec ou sans paramètres supplémentaires) et l'appel des méthodes du plugin (avec ou sans paramètres).

Si vous n'êtes pas un expert, je vous recommande de n'apporter aucune modification à cette méthode.

Toutes les instances du plugin sont traitées par un unique objet méthodes, chaque instance (jObj : un objet jQuery) doit par conséquent stocker une copie de son état (les options) dans jObj.data( objMethods._copyright.objMethodsName ).

Cet état est transmis à l'objet méthodes au début de l'action. Il est alors stocké temporairement dans l'objet options de l'objet méthodes. Lorsque l'action est terminée, l'état est sauvegardé dans l'instance et l'objet méthodes n'en conserve plus nulle trace.

Lors de l'initialisation, l'état de l'instance est constitué d'une copie des options par défaut et des options et paramètres transmis par l'utilisateur du plugin à la fonction d'initialisation de l'objet méthodes.

Chaque instance doit travailler sur sa copie des arguments (objMethods, method, params).

dvjhPluginCreate
CacherSélectionnez

III-C. Squelette d'un objet méthodes

Les propriétés, les méthodes et les objets précédés d'un tiret bas sont privés !

Un objet méthodes doit contenir au minimum :
  • les objets privés : _copyright et _options ;
  • les objets publics : jObj et options ;
  • les méthodes publiques : init, destroy, setOption et getOption.
L'espace de nom, qui contient l'objet méthodes, doit offrir :
  • un accès, en lecture, à la propriété _copyright.objMethodsName ;
  • une fonction gérant la modification des options par défaut (_options).

Pour simplifier l'écriture d'un objet méthodes, j'ai construit un « squelette », que j'ai dénommé dvjhSqueletteMethods. Il permet de construire l'ébauche d'un nouvel objet méthodes par copier-coller sans oublier aucun des objets et des méthodes qu'il doit impérativement contenir.

À l'usage, vous trouverez sans doute les commentaires dans dvjhSqueletteMethods surabondants et gênants, mais je vous conseille vivement de les conserver en production au bénéfice de la personne qui modifiera vos plugins.

dvjhSqueletteMethods
CacherSélectionnez

Rappel : pour tester les codes, veuillez charger le ZIP.

Examinons le code jQuery du fichier dvjhSquelette.html :

DvjhSquelette.html
CacherSélectionnez

Le code d'utilisation d'un dvjhPlugin doit toujours se trouver dans un bloc « try catch », c'est l'ultime rempart avant le crash global.

Extrait du code HTML
CacherSélectionnez
  1.     <!-- 
  2.         Attention ! Le contenu de l'attribut data-plugin-options 
  3.         doit impérativement être un JSON bien formé sous peine 
  4.         d'être ignoré par JSON.parse(). 
  5.     --> 
  6.     <div id="divTest3" data-plugin-options='{ "class" : "surpriseClass" }'> 
  7.         <p>Un mot pour remplir</p> 
  8.     </div> 

Ne surchargez pas inutilement l'objet _options (les options par défaut) ! Je n'ai rencontré aucun problème de performance avec le stockage des options dans l'instance lors de mes tests, mais pour des données encombrantes et communes à toutes les instances, il est important de se souvenir que dans le code de l'objet méthodes vous avez accès au contenu de la clôture jQuery.

IV. Les exemples

Commençons par un exemple simple. Un plugin multitâche qui gère la modification de ses options.

IV-A. Le plugin dvjhTourne

Dans le tutoriel « Adapter jQuery à vos besoins », le plugin tourne est un bon candidat car pour gérer les modifications de son option « degrees » il avait besoin de deux autres plugins : tourneSetOptions et tourneGetOptions. De plus, toutes les instances du plugin tourne utilisaient la même option.

dvjhTourne
CacherSélectionnez

Rappel : pour tester les codes, veuillez charger le ZIP.

Utilisation du plugin, extrait de dvjhTourne.html :

DvjhTourne.html
CacherSélectionnez

IV-B. Le plugin dvjhEventShow

Image personnelleOn crée un nouveau fragment du DOM, jObjText, et on le stocke dans les options de l'instance. Ce fragment du DOM initialise trois gestionnaires d'événements (click, mouseenter et mouseleave) qui manipulent l'état de l'instance.
Bien entendu, lorsqu'un événement se produit l'objet méthodes est inactif et this.jObj a la valeur « null » !
On est donc obligé d'activer l'objet méthodes à partir du gestionnaire d'événements.
Deux méthodes possibles, soit :
  1. Stocker la valeur de this.jObj par un $( l'élément du DOM qui intercepte l'événement ).data( "dvjhEventShowElement", this.jObj[0] ) ;
  2. Pour les cas simples, retrouver this.jObj à partir de l'élément du DOM qui intercepte l'événement. Ici: $( this ).parent().

Dans dvjhEventShow le premier cas est mis en commentaires, mais il est utilisable.
dvjhEventShow
CacherSélectionnez

Cet exemple montre également comment gérer la destruction d'une instance.

Rappel : pour tester les codes, veuillez charger le ZIP.

Utilisation du plugin, extrait de dvjhEventShow.html :

dvjhEventShow.html
CacherSélectionnez

IV-C. Le plugin dvjhStorage

Ce plugin n'agit pas sur l'interface utilisateur, mais qui dit multitâche implique la complexité.

Le plugin dvjhStorage gère simplement le stockage de données dans un objet jQuery (jObj.data( "dvjhStorage" )), dans le sessionStorage et dans le localStorage des navigateurs non obsolètes.

Bien entendu on peut ajouter, supprimer ou chercher des données. Mais on peut aussi copier ou transférer des données d'un espace de stockage à l'autre.

Chaque donnée est représentée par un couple « key, value ». La variable « key » doit être du type « string ». La variable « value » peut contenir n'importe quoi car elle sera sauvegardée au format JSON. Lorsque la donnée est extraite du stockage, elle est restaurée dans son type d'origine. Lorsque la donnée est copiée ou transférée dans un autre stockage elle reste au format JSON.

Firefox : le sessionStorage n'est pas disponible en « local mode ».

On sauvegarde des données au format JSON. Si l'on passe un objet qui contient une fonction elle sera supprimée de la sauvegarde. De même toute clé (key) qui n'est pas un texte (string) ou un nombre (number).

Méthode Signature
initialisation jObj = dvjh.initStorage( selector );
set jObj.dvjhStorage( "set", ( "data" || "session" || "local" ), key, value );
get jObj.dvjhStorage( "get", ( "data" || "session" || "local" ), key [, callback] );
has jObj.dvjhStorage( "has", key [, callback] );
remove jObj.dvjhStorage( "remove", ( "data" || "session" || "local" ), key );
copy jObj.dvjhStorage( "copy", origine ( "data" || "session" || "local" ), destination ( "data" || "session" || "local" ), key );
relocate jObj.dvjhStorage( "relocate", origine ( "data" || "session" || "local" ), destination ( "data" || "session" || "local" ), key );
clear jObj.dvjhStorage( "clear", ( "data" || "session" || "local" ) );
clearAll jObj.dvjhStorage( "clearAll" );
dvjhStorage
CacherSélectionnez

Rappel : pour tester les codes, veuillez charger le ZIP.

Utilisation du plugin, extrait de dvjhStorage.html :

dvjhStorage.html
CacherSélectionnez

V. Remerciements

Un grand merci à ceux qui auront le courage de lire et de tester mon travail !

Un grand merci au correcteur Claude LELOUP pour son excellent travail.