Treeview est un contrôle graphique de la librairie Yahoo! UI Library représentant une arborescence. Il en existe des tas bien sûr (il n'y a qu'à googler un peu pour s'en convaincre), mais ce composant est doté de qualités remarquables qui le rendent particulièrement attractif :
- Pour commencer, il s'intègre parfaitement avec l'ensemble de la librarie YUI. C'est une évidence, mais ce n'est pas pour autant inutile de le préciser. Ce n'est pas toujours facile de faire cohabiter plusieurs librairies ensemble, et c'est donc une difficilté en moins (essayez donc de faire une page avec Prototype et Jsval par exemple1, vous allez vous amuser). De même, le fait qu'il soit développé par des gens comme Yahoo! laisse planer peu de doutes sur son futur ainsi que son support.
- Il est fourni avec une API très riche, ce qui permet d'étendre ses fonctionnalités comme d'intéragir avec chacun des éléments composant l'arborescence avec (une relative) simplicité.
- Il est particulièrement bien documenté : utilisation rapide, description de l'API, exemples détaillés.
- Enfin, il propose des fonctionnalités avancées qui font défaut à la plupart de ses congénères, comme le (re)chargement dynamique des noeuds (avec de l'AJAX en arrière-plan).
Nous avons donc là un composant de grande qualité, et les trois astuces suivantes vont vous permettre d'en améliorer le rendu visuel.
Astuce n°1 : Associer des icônes aux branches
Ce n'est pas réellement une astuce nouvelle puisqu'elle est présentée dans les exemples, mais elle fera une bonne introduction à ce qui va suivre. Par défaut, Treeview affiche une branche (un noeud possédant des enfants, par opposition à une feuille) avec des symbôles + et - assez classiques (
). Pour plaire au boss ainsi qu'aux utilisateurs, on peut avoir envie d'y ajouter des icônes de dossiers.
Les développeurs de Treeview ont prévu le coup, et c'est donc quelque chose de très simple à mettre en oeuvre. Lesdites icônes sont en réalité des cellules de tableau vides, de largeurs fixes, et composées d'images de fond. Il y a 11 classes définies, auxquelles correspondent 11 images, en fonction de l'état de la branche (pliée, dépliée, surlignée, etc), en voilà par exemple une :
/* first or middle sibling, collapsable */
.ygtvtm {
width:16px; height:22px;
cursor:pointer ;
background: url(treeview/tm.gif) 0 0 no-repeat;
}
...
Il suffit donc de redéfinir ces classes pour modifier l'aspect visuel des branches. Dans notre cas, on augmente la largeur des cellules et on change l'image de fond :
/* first or middle sibling, collapsable */
.ygtvtm {
width:34px; height:22px;
cursor:pointer ;
background: url(tree/tm-folder.gif) 0 0 no-repeat;
}
...
Et voilà le résultat :

Astuce n°2 : Associer des icônes aux feuilles
On a associé des icônes aux branches, on peut maintenant vouloir en associer aux feuilles. Treeview propose deux types de noeuds : des noeuds texte (TextNode) et des noeuds HTML (HTMLNode). Le premier prend un label et éventuellement une URL pour paramètres, et Treeview se charge de créer le noeud avec, le second accepte du code HTML "brut". J'entends par "brut" qu'il est injecté tel quel, ce qui pour notre cas n'est pas forcément très intéressant, puisque requerrait alors de créer les liens à la main (et tous les événements et attributs qui vont avec). Nous allons donc rester avec les noeuds texte.
Treeview rend un noeud texte de la façon suivante (je n'ai conservé que les infos utiles) :
<a onmouseout="..." onmouseover="..." onclick="..." href="..." class="ygtvlabel" id="ygtvlabelel1">Répertoire 1</a>
Sur le même principe que l'astuce précédente, on peut imaginer modifier la classe ygtvlabel du lien pour ajouter une image de fond ainsi qu'un padding à gauche.
/* the style of the text label in ygTextNode */
.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover {
margin-left:2px;
text-decoration: none;
}
Problème : la classe ygtvlabel est appliquée aussi bien sur les branches que sur feuilles. Une solution alternative est donc d'affecter à tous nos noeuds de type feuille une seconde classe qui se chargera du padding et de l'image de fond :
#treeview a.file {
background: transparent url(tree/file.gif) no-repeat top left ;
padding-left: 19px ;
}
Et à la création du noeud, on ajoute cette classe spécifique à l'aide de la propriété TextNode.labelStyle, qui définit le style du lien :
var node = new YAHOO.widget.TextNode({ label: "Darkmag.net", href: "http://darkmag.net"}, parentNode, false);
node.labelStyle += " file" ;
Allons un peu plus loin. Imaginons que l'on souhaite représenter des fichiers dans cette arborescence. Il deviendrait du coup assez intéressant d'avoir une icône propre au type du fichier. Modifions la règle CSS précédente de façon suivante :
#treeview a.file {
background: transparent url(tree/ext/unknown.gif) no-repeat top left ;
padding-left: 19px ;
}
#treeview a.doc {
background-image: url(tree/ext/doc.gif) !important;
}
#treeview a.xls {
background-image: url(tree/ext/xls.gif) !important;
}
...
Et changeons l'affectation du style comme suit :
node.labelStyle += " file " + getExtensionFichier() ;
Avec getExtensionFichier() qui retourne l'extension du fichier à représenter dans l'arbre (à définir, bien sûr). Les feuilles ont alors 3 classes, mais la dernière peut ne peut avoir de définition dans la feuille de style. Dans l'affirmative, c'est l'icône du type de fichier qui sera affichée (car la priorité est supérieure grâce au !important), dans le cas contraire, c'est l'icône "fichier inconnu". Voilà le résultat :

Astuce n°3 : Spécifier des styles spécifiques pour certaines branches
Là, ça devient un peu plus compliqué, car Treeview n'a pas vraiment été conçu dans cette optique : il y a 11 classes définies, et elles sont attribuées systématiquement à chaque branche, point. On peut les modifier (comme on l'a fait dans l'astuce n°1), mais c'est répercuté sur l'ensemble des branches de l'arbre. Et la propriété TextNode.labelStyle ne concerne que le lien des feuilles. L'objectif est de pouvoir attribuer une classe spécifique pour une branche donnée. Et ça, ce n'est pas prévu à la base.
Comme je le disais précédemment, Treeview est fourni avec une API très riche, et c'est bien ça qui va nous sauver. Nous allons créer une nouvelle classe, héritant de toutes les propriétés d'une branche classique, mais qui possède un paramètre supplémentaire : une classe CSS spécifique (enfin, un suffixe, pour être plus précis). Dans un premier temps, il convient de faire un nouveau jeu de 11 images et de 11 classes exploitant cette nouvelle image :
/* first or middle sibling, collapsable */
.files-ygtvtm,
.ygtvtm {
width:34px; height:22px;
cursor:pointer ;
background: url(tree/tm-folder.gif) 0 0 no-repeat;
}
.files-ygtvtm {
background-image: url(tree/tm-files.gif) !important;
}
...
Vient la partie amusante : la création de la nouvelle classe, sobrement intitulée CustomCssTextNode :
// constructeur de CustomCssTextNode
YAHOO.widget.CustomCssTextNode = function(sCssPrefix, oData, oParent, expanded) {
this.sCssPrefix = sCssPrefix;
YAHOO.widget.CustomCssTextNode.superclass.constructor.call(this, oData, oParent, expanded);
return true ;
}
// héritage
YAHOO.extend(YAHOO.widget.CustomCssTextNode, YAHOO.widget.TextNode);
// et on surcharge de la méthode getStyle()
YAHOO.widget.CustomCssTextNode.prototype.getStyle = function() {
return this.sCssPrefix + YAHOO.widget.CustomCssTextNode.superclass.getStyle.call(this) ;
}
La première partie est le constructeur de cet classe. Il prend les mêmes paramètres que TextNode, plus un : le préfixe de la classe CSS. On copie donc la valeur de sCssPrefix en paramètre dans un nouvel attribut (que TextNode ne possède pas), puis on appelle le constructeur de TextNode. Une fois la classe déclarée, on la fait hériter de toutes les méthodes et propriétés de TextNode grâce à la méthode YAHOO.extend(). Enfin, on surcharge la méthode getStyle() qui s'avère être, après un peu d'introspection dans le code de Treeview, la méthode appelée par Treeview pour indiquer la classe CSS de la branche, et on la fait retourner la chaîne préfixe + style-normalement-défini. Et c'est tout.
A l'usage, la création d'une telle branche se fait tout à fait naturellement, comme on peut le voir ci-dessous :
var branch1 = new YAHOO.widget.TextNode({ label: "Répertoire1" }, parentNode, false);
var branch11 = new YAHOO.widget.CustomCssTextNode("files-", { label: "Répertoire 1.1" }, branch1, false);
Le premier paramètre de CustomCssTextNode est le préfixe de la classe CSS, dont on a défini les classes précédemment. Et voilà le résultat :

Conclusion
Au lieu des quatre astuces prévues initialement vous n'en aurez eu que trois, faute de temps (et un peu de motivation, je dois bien l'admettre), mais j'espère que cela vous suffira à vous convaincre du potentiel de la bête, et vous donnera envie d'essayer - si ce n'est déjà fait - la librairie YUI, qui est de loin ma préférée parmi les quatre qui font beaucoup parler d'elles en ce moment. Et à venir prochainement un billet sur l'intégration de Treeview avec rechargement dynamique sur plateforme Domino (sans agent, sinon ça serait trop facile) quand je l'aurais terminé (je le prédis pour avant ceux sur FreeBSD ;).
1 : Ces librairies définissent toutes les deux un objet Field dans le namespace global.