I. Objectif

Tous les sites Web et applications bien rédigés qui sont interactifs ont le même modèle de base :

Image non disponible

Chaque page d'un site ou d'une application que vous construisez est ésotérique. Il peut y avoir n'importe quelle combinaison d'éléments interactifs, certains étant interactifs les uns avec les autres (par exemple, une validation de formulaire pourrait interagir avec un contrôleur Ajax pour empêcher d'envoyer un formulaire qui n'est pas valide).
Typiquement, ce code de cohésion existe dans un état de DOMReady. En gros, il faut sélectionner le formulaire et instancier telle classe avec telles options. Ce code est fragile ; si vous changez le DOM ou le code, l'état se casse facilement. Ce n'est pas réutilisable, cela fonctionne seulement pour un état spécifique de la page.

La bibliothèque proposée essaye d'extraire le code du DOMReady pour constituer quelque chose que vous écrivez seulement une fois et employez souvent. C'est rapide et facile à adapter et à étendre. Au lieu d'avoir un bloc DOMReady qui, par exemple, trouve toutes les images d'une page et les met dans une galerie, ou qui, dans un autre bloc, recherche la cible pour tous les liens sur la page et les transforme en info bulles, la bibliothèque Behavior fait une simple recherche de tous les éléments que vous avez marqués. Chaque élément est passé par un filtre. Un filtre est une fonction (avec peut-être une certaine configuration) que vous avez nommée. Chacune de ces fonctions prend l'élément, lit ses propriétés d'une façon prescrite et appelle le composant UI (interface utilisateur) approprié.

II. Pourquoi ?

En bref, au lieu d'avoir une fonction DOMReady qui trouve les éléments dans votre DOM et instancie des classes et autres, vous mettez la configuration dans le HTML lui-même et vous écrivez le code de la méthode new Foo(...) seulement une fois. Exemple :


JavaScript :

 
Sélectionnez
new Behavior().apply(document.body);
new Delegator().attach(document.body);

HTML :

 
Sélectionnez
<div data-behavior="Accordion" data-accordion-options="
  'headers': '.toggle',
  'sections': '.target'
">
 <div class="toggle">Toggle 1</div>
 <div class="target">This area is controlled by Toggle 1.</div>
 <div class="toggle">Toggle 2</div>
 <div class="target">This area is controlled by Toggle 2.</div>
 <div class="toggle">Toggle 3</div>
 <div class="target">This area is controlled by Toggle 3.</div>
</div>

<hr/>
<div id="box">
  <p>
      <a data-trigger="toggleClass" data-toggleclass-options="
          'target': '!div#box',
          'class': 'red'
      ">Click to toggle this color to red and back.</a>
  </p>
</div>

CSS :

 
Sélectionnez
.red {
  background: #DE3535;
}
a {
  display: block;
  cursor: pointer;
}
.toggle {
  cursor:pointer;
  background: #777;
  padding: 2px;
}
.target {
  padding: 4px;
}
[data-behavior=Accordion] {
  width: 300px;
  margin: 10px;
}
#box {
  border: 1px solid #000;
  margin: 10px;
  text-align: center;
  display:inline-block;
}
[data-trigger=toggleClass] {
  width: 100px;
  padding: 40px;
}



Donc, au lieu de ceci :

 
Sélectionnez
$$('form').each(function(form){
 new FormValidator(form, someOptions);
 new Form.Request(form, someOptions);
});
new Tips($$('.tip'));
$$('.accordion').each(function(container){
 new Accordion(container.getElements('.toggler'), container.getElements('.section'), someOptions);
});
//etc.


Vous faites :

 
Sélectionnez
<form data-behavior="FormValidator FormRequest" data-formvalidator-options="{someOptions}">...</form>
<a data-behavior="Tip" title="I'm a tip!">blah</a>
<div data-behavior="Accordion" data-accordion-options="{someOptions}">...</div>


Pensez à cela comme à une délégation (comme dans la délégation d'événementsdélégation d'événements) pour l'invocation de classe. Si vous employez le DOMReady pour faire votre initialisation et que vous voulez permuter de l'HTML via un appel Ajax, vous devez réappliquer cette initialisation de manière sélective pour les composants que vous mettez à jour uniquement, ce qui est souvent douloureux. Pas avec Behavior. Vous appliquez simplement les filtres à la réponse.

Vous faites beaucoup moins de sélection dans le DOM ; vous ne lancez qu'une fois $$('[data-behavior]') (cependant, certains filtres peuvent lancer plusieurs sélecteurs sur eux-mêmes - comme l'accordéon trouvant ses togglers et sections).

L'initialisation du DOMReady est toujours étroitement liée aux DOM de toute façon, mais elle est également séparée de lui. Si vous changez le DOM, vous pourriez casser le JS et toujours devoir le resynchroniser. Vous ne devez presque pas faire cela ici parce que le DOM et sa configuration sont étroitement liés et sont au même endroit.

Les développeurs qui ne sont peut-être pas intéressés par l'écriture de composants n'ont pas besoin de patauger dans le JS pour l'utiliser. C'est une véritable affaire si vous travaillez avec une équipe que vous devez soutenir.

Behavior est conçu pour les applications qui mettent constamment à jour l'UI avec de nouvelles données du serveur. Ce n'est toutefois pas un remplaçant du MVC. La bibliothèque est conçue pour le développement Web qui emploie du code HTML et non des API JSON (cependant, ça peut être amusant de jouer avec). Si vous détruisez un nœud qui a un composant initialisé, il est facile de s'assurer que le composant se nettoie lui-même. La bibliothèque permet également d'empêcher une mauvaise configuration et propose une API qui facilite la lecture des valeurs de la configuration (on en reparle plus loin).

Il y a quelques autres astuces ; vous obtenez librement des spécifications de tests et des modèles parce que le code pour les créer tous les deux est dans le filtre du Behavior. Voici un exemple de ce qu'il faut pour écrire une spécification sur un composant et ÉGALEMENT sur la manière de l'instancier (ceci emploie Behavior.SpecsHelpers.jsBehavior.SpecsHelpers.js).

 
Sélectionnez
Behavior.addFilterTest({
 filterName: 'OverText',
 desc: 'Creates an instance of OverText',
 content:  '<input data-behavior="OverText" title="test"/>',
 returns: OverText
});


Ce code ci-dessus peut être employé pour valider que la partie de HTML impliquée crée, en fait, une instance d'OverText ; il peut également être employé avec Benchmark.jsBenchmark.js pour voir lesquels de vos filtres sont les plus coûteux.

III. Delegator

La bibliothèque inclut également un fichier appelé Delegator qui est essentiellement la même chose que le Behavior mais pour les événements. Par exemple, disons que vous avez un modèle qui, une fois que l'on clique sur un lien, va cacher un élément parent. Plutôt que d'écrire ce code à chaque fois :

 
Sélectionnez
document.body.addEvent("click:a.hideParent", function(e, link){
 e.preventDefault();
 link.getParent().hide();
});


Vous enregistrez ce modèle auprès du Delegator et vous n'avez plus qu'à faire :

 
Sélectionnez
<a data-trigger="hideParent" data-hideparent-options ="{'target': '.someSelector'}">Hide Me!</a>


Il fournit essentiellement la même valeur que le Behavior, mais au moment d'un événement. L'exemple ci-dessus est assez basique, on est d'accord. Mais considérez combien de ces petites choses vous écrivez pour faire une fonctionnalité d'une application Web. Si vous pouvez les créer une fois et les configurer en ligne, vous vous épargnez beaucoup de lignes de code.

IV. BehaviorAPI

Cette bibliothèque autonome facilite la lecture des propriétés des données et des éléments. Les exemples des expressions HTML évaluées sont comme suit (ils produisent tous le même résultat) :

 
Sélectionnez
<tag data-behavior="Filter1 Filter2" data-filter1-options="{'opt1': 'foo', 'opt2': 'bar', 'selector': '.selector'}"> //prefered
<tag data-behavior="Filter1 Filter2" data-filter1-options="'opt1': 'foo', 'opt2': 'bar', 'selector': '.selector'"> //no braces on JSON
<tag data-behavior="Filter1 Filter2" data-filter1-options="{'opt1': 'foo', 'opt2': 'bar'}" data-filter1-selector=".selector">
<tag data-behavior="Filter1 Filter2" data-filter1-opt1='foo' data-filter1-opt2='false' data-filter1-selector=".selector">


La valeur de l'option est d'abord analysée comme un objet JSON (il est légèrement plus laxiste dans la mesure où vous ne devez pas l'envelopper dans des {} juste pour la convenance). Les valeurs définies ici sont lues afin de permettre d'exprimer des tableaux, des nombres, des booléens, etc. Les fonctions et les rappels (callbacks) ne sont généralement pas employés par Behavior.

Si vous essayez de lire une valeur qui n'est pas définie dans cet objet d'options, le code tente alors de lire le nom de la propriété directement (par exemple data-behaviorname-prop). Cette valeur est toujours une chaîne de caractères à moins que vous ayez spécifié un type. Si un type est spécifié, la valeur est lue par le parseur JSON et est validée en fonction de ce type.

Même si vous ne voulez pas utiliser toutes les fonctionnalités de la bibliothèque Behavior, cette bibliothèque peut être utile si vous aimez l'idée d'inclure de la configuration en ligne. Il y a beaucoup plus de choses dans l'API BehaviorAPI. Il vaut donc mieux parcourir attentivement la documentationdocumentation.

V. Documentation

VI. Comportements courants

VII. Démos

VIII. Remarques

Voici quelques remarques concernant l'implémentation. La documentation devrait être lue probablement en premier lieu parce qu'elle donne des exemples d'utilisation.

  • Seulement un sélecteur à la fois ; ajouter 1000 filtres n'affecte pas les performances.
  • Les nœuds peuvent avoir de nombreux filtres.
  • Les nœuds peuvent avoir un nombre arbitraire d'options supportées pour chaque filtre (data-behaviorname-foo="bar").
  • Les nœuds peuvent définir des options en JSON (c'est actuellement l'implémentation conseillée - data-behaviorname-options="your JSON").
  • Les éléments peuvent être supprimés avec une destruction configurée ; nettoyer un élément nettoie également tous les enfants de cet élément qui appliquent le même comportement.
  • Les comportements sont seulement appliqués une fois à un élément ; si vous appelez myBehavior.apply(document.body) une douzaine de fois, les éléments avec des filtres auront ces filtres appliqués seulement une fois (on peut forcer à réappliquer le comportement).
  • Les filtres sont des instances de classes qui sont appliquées à tout nombre d'éléments. Ils ont un nom unique.
  • Les filtres peuvent avoir un espace de nom. Déclarez un filtre appelé Foo.Bar et référencez ses options comme suit : data-foo-bar-options="...".
  • Il y a des filtres "globaux" qui sont enregistrés pour toutes les instances d'un comportement.
  • Les instances de filtres obtiennent la priorité. Ceci tient compte du fait que les bibliothèques fournissent des filtres (comme http://github.com/anutron/more-behaviorshttp://github.com/anutron/more-behaviors). Ceci est nécessaire pour qu'une instance spécifique le réécrive sans affecter l'état global (ce modèle est dans le Form.Validator de MooTools et fonctionne assez bien).

IX. Limitations

À cause de la recherche DOM pour la création et la destruction, vous ne pouvez pas avoir des exemples de comportements inclus l'un dans l'autre.

X. Téléchargement

Vous pouvez trouver la bibliothèque Behavior sur githbgithb et aussi sur ClientcideClientcide où vous trouverez également un constructeur. Celui-ci vous fournit un stock de comportements provenant du Clientcide et ceux dont j'ai donné l'autorisation pour le MooTools More. Si vous voulez prendre le constructeur par défaut, vérifiez bien de sélectionner "MooTools Boostrap" dans le menu du haut (ou bien cliquez icihttp://dev.clientcide.com/?version=MooTools%20Bootstrap).

XI. Remerciements

Je tiens à remercier chaleureusement Aaron Newton de m'avoir autorisé à traduire son article.
Mes remerciements vont également à _Max_ et ClaudeLELOUP qui me relisent consciencieusement.