Réaliser un catalogue interactif (MSIE5 "natif")
Création de la page : 2001-03-02
Nous nous proposons de réaliser le catalogue d'une bibliothèque
électronique, par exemple celui d'une bibliothèque des principaux
documents relatif aux NTIC que nous aurions constituée sur un CD-ROM.
(Ce pourrait tout aussi bien être un catalogue de liens pointant
directement vers les originaux de ces documents sur le WWW.)
Voir l'exemple réalisé
Notre fichier XML sera de la forme suivante :
<catalogue>
<categorie>
<nom>XSLT</nom>
<aide>Le langage qui permet de transformer
du XML en n'importe quoi d'autre (XML, HTML, texte...). A découvrir
absolument.</aide>
<categorie>
<nom>Spécifications</nom>
<document lang="fr">
<titre>Spécification XSLT</titre>
<ref>xslt/spec/tradfr/Transformations XSL (XSLT).htm</ref>
<auteur>W3C</auteur>
</document>
</categorie>
<categorie>
<nom>Référence</nom>
<document lang="en">
<titre>Référence XSLT</titre>
<ref>XSLT\Reference\Zvon\Output\index.html</ref>
<auteur>Miloslav Nic</auteur>
</document>
</categorie>
<categorie>
<nom>Tutoriels</nom>
<document lang="fr">
<titre>Tutoriel XSLT</titre>
<ref>XSLT\Tutoriels\U-Picardie\coursxsl.html</ref>
<auteur>U. de Picardie</auteur>
</document>
<document lang="en">
<titre>Tutoriel XSLT</titre>
<ref>XSLT\Tutoriels\Walsh\xsl_tutorial.pdf</ref>
<auteur>Norman Walsh & Paul Grosso</auteur>
</document>
<document lang="en">
<titre>Chap 14 (XSLT) de la "XML Bible"</titre>
<ref>XSLT\Tutoriels\XML Bible\XSL Transformations.htm</ref>
<auteur>Elliotte Rusty Harold</auteur>
</document>
</categorie>
</categorie>
</categorie>
</catalogue>
Où l'on voit que notre catalogue est simplement subdivisé
en catégories, elles-mêmes subdivisées en (sous-)catégories,
et ainsi de suite ad libitum -- pour arriver enfin à des
documents caractérisés par un auteur, un titre, une adresse,
et une langue.
Notre feuille de style XSLT va se charger de l'affichage de ce catalogue,
et va permettre à l'utilisateur de l'exploiter de deux façons
différentes :
-
par navigation directe dans le catalogue au moyen d'arborescences
dépliables et repliables ;
-
par sélections successives dans des listes déroulantes
dynamiquement mises à jour.
Réaliser une arborescence dépliable et repliable
Le principe est d'emballer chaque titre de catégorie dans
une balise HTML <DIV CLASS="wrapping" >,
la classe "wrapping" étant associée par style CSS à
une certaine valeur de marge gauche. L'imbrication de ces balises DIV va
donc provoquer un décalage supplémentaire vers la droite
à chaque fois que l'on passera d'une catégorie à ses
sous-catégories.
Chaque titre de catégorie est par ailleurs affiché précédé
des signes "+" et "-", l'un et l'autre encadré par une balise <SPAN>.
A tout moment l'une ou l'autre de ces balises sera affectée du style
CSS display:none, ce qui la rendra invisible.
Derrière chaque titre de catégorie, et toujours à
l'intérieur de la même balise <DIV
CLASS="wrapping"> d'emballage "supérieur", vient s'insérer
une autre balise <DIV CLASS="wrapping">
d'emballage "inférieur". Cette nouvelle balise est destinée
à emballer :
-
les éventuelles sous-catégories de la catégorie (<xsl:apply-templates
select="categorie"> ) et/ou
-
les éventuels ouvrages appartenant à la catégorie
(<xsl:apply-templates select="document">)
Par défaut elle est affectée du style CSS display:none,
ce qui la rend invisible, ainsi que tout son contenu.
<xsl:template match="categorie">
<DIV CLASS="wrapping" >
<DIV CLASS="title" onclick="toggleDisplay(this)">
<SPAN>+</SPAN><SPAN
STYLE="display:none">-</SPAN>
<xsl:value-of select="nom"/>
</DIV>
<DIV CLASS="wrapping" STYLE="display:none">
<xsl:apply-templates select="categorie"
order-by="nom"/>
<xsl:if test="document">
<table class="persons">
<tr><th>titre</th><th>auteur</th></tr>
<xsl:apply-templates
select="document" order-by="titre"/>
</table>
</xsl:if>
</DIV>
</DIV>
</xsl:template>
Le fait de cliquer le titre de la catégorie va provoque le lancement
de la fontion toggleDisplay() ci-dessous
:
function toggleDisplay(titre) {
plus = titre.children[0];
if (plus.style.display=="")
// le signe plus est visible : il faut déplier
{
deplirepli(titre, deplier);
}
else
// replier
{
deplirepli(titre, replier);
}
}
Cette fonction fait elle-même appel à la fonction deplirepli()
qui admet deux paramètres. Le premier correspond au titre qui a
été cliqué, le second est un booléen qui indique
s'il faut replier ou déplier :
var deplier = true;
var replier = false;
function deplirepli(titre, depli) {
plus = titre.children[0];
moins = titre.children[1];
div = titre.parentElement;
moins.style.display = depli ? "" : "none";
plus.style.display = depli ? "none" : "";
titre.title="Cliquer pour " + (depli ? "re" : "dé") + "plier";
div.children[1].style.display= depli ? "" : "none";
}
Selon qu'il s'agit de déplier ou de replier, le signe + ou le
signe moins va être rendu visible (et l'autre rendu invisible), et
l'élément d'emballage inférieur <DIV
CLASS="wrapping" > qui suit le titre (et qui est le deuxième
enfant de l'élément d'emballage supérieur) va être
rendu visible ou invisible.
Réaliser une sélection par listes déroulantes interactives
Le principe de cette sélection est le suivant : un premier ensemble
de trois contrôles va permettre d'effectuer des sélections
:
-
une première liste déroulante énumère des catégories
de "haut" niveau ou thèmes (HTML, XML, XSLT...),
-
une deuxième liste énumère des catégories de
"bas" niveau (Spécifications, Références, Tutoriels...),
-
une case à cocher permet de sélectionner la langue (toutes
langues/français seulement)
Et le résultat de toutes ces sélections (= la liste des documents
pertinents) va apparaître dans une troisième liste déroulante,
la liste résultat. Il restera à l'opérateur
à sélectionner l'un des documents de cette liste et
à cliquer le bouton "Voir...". L'arborescence de la bibliothèque
va alors être complètement repliée ; la branche correspondant
au document sélectionné va être sélectivement
dépliée ; et le contrôle va être passé
à la ligne affichant le document sélectionné.
Les deux premières listes de sélection sont générées
à partir du code HTML suivant :
Thèmes :
<select ID="themes" onchange="selectfeature()">
<option value="nom='Généralités'">Généralités</option>
<option value="nom='HTML'or nom='CSS'">HTML/CSS</option>
<option value="nom='XML'">XML</option>
<option value="nom='XSLT' or nom='XPath'">Transformations XML</option>
<option value="nom='DOM'">API XML</option>
<option value="nom='XLink' or nom='Xpointer' or nom='XPath'">Liens
XML</option>
<option value="nom='RDF'">Web sémantique</option>
<option value="nom='SOAP'">Objets distribués</option>
<option value="" selected = "selected">Tout</option>
</select>
Catégories :
<select ID="categories" onchange="selectfeature()">
<option value="nom='Référence'">Référence</option>
<option value="nom='Spécifications'">Spécifications</option>
<option value="nom='Tutoriels'">Tutoriels</option>
<option value="nom='Outils'">Outils</option>
<option value="nom='Applications'">Applications</option>
<option value="nom='EDI'">EDI</option>
<option value="" selected = "selected">Tout</option>
</select>
On voit qu'à chaque sélection possible, chacune de ces listes
fait correspondre une valeur (value) sous forme d'une chaîne
de caractère qui va servir ensuite à construire une partie
de l'expression XPath que nous allons utliser pour rechercher dans notre
catalogue.
La troisième liste de sélection, elle, est construite
dynamiquement par transformation XSLT. Au chargement du fichier
XML, la feuille de style XSLT comporte en effet les instructions suivantes
:
<select ID="liste" selected = "selected" onchange="changeButton(this)">
<xsl:for-each select="//categorie[nom]//document" order-by="titre">
<option>
<xsl:attribute name="value"><xsl:eval>uniqueID(this)</xsl:eval></xsl:attribute>
<xsl:value-of select="titre"/>, par <xsl:value-of select="auteur"/></option>
</xsl:for-each>
</select>
Au chargement du fichier XML, la liste résultat comporte
donc l'ensemble des documents.
Par ailleurs, la valeur associée (value) associée
à chacun de ces documents correspond à un identificateur
unique attribué par XSLT à l'élément <document>
correspondant. Identificateur unique qui est par ailleurs utilisé
pour associer une cible HTML à la ligne correspondant au document,
ce qui permettra le saut direct à cette ligne, par lien hypertexte
quand un document est sélectionné :
<a><xsl:attribute name="name"><xsl:eval>uniqueID(this)</xsl:eval></xsl:attribute></a>
La mise à jour de la liste résultat va être
effectuée par la fonction selectfeature(),
appelée à chaque fois qu'une modification est apportée
à l'un quelconque des trois contrôles de sélection.
Cette fonction va construire une expression XPath, par concaténation
des chaînes de caractère suivantes
-
//, pour indiquer que la recherche commence
à la racine du document et descend un nombre quelconque de niveaux
;
-
un premier "test de noeud", qui va rechercher le thème sélectionné
par la première liste de sélection (ex categorie[nom='XSLT'
or nom='XPath'] ;
-
//, pour indiquer que l'on descend un nombre
quelconque de niveaux ;
-
un second "test de noeud", qui va rechercher la sous-catégorie sélectionnée
par la première liste de sélection (ex categorie[nom='Spécifications']
;
-
//, pour indiquer que l'on descend encore
un nombre quelconque de niveaux ;
-
s'il y a lieu, un troisième "test de noeud" document[@lang="fr"]
pour ne sélectionner que les documents en français.
Les deux premiers tests de noeud sont construits à partir des chaînes
de caractères (ex. nom='HTML'or nom='CSS')
renvoyées par les deux listes déroulantes de sélection.
L'expression XPath va être ensuite écrite dans la feuille
de style XSLT, à l'emplacement correspondant à la valeur
de l'attribut select de la balise <xsl:for-each>
qui sert à remplir la liste de sélection. Elle est également
utilisée pour calculer le nombre de documents sélectionnés
(nombreDocs='selectNodes("'+pattern+'").length')
et pour mettre à jour, toujours dans la feuille de style, la légende
de la liste de sélection. Et l'on va ensuite relancer la transformation
XSLT pour mettre à jour l'affichage.
Mettre à jour partiellement un affichage HTML
L'astuce mise en oeuvre ici consiste à ne modifier qu'une partie
du HTML affiché, à savoir le "groupe de champs" (fieldset),
d'attribut ID selection (document.forms(0).selection.outerHTML),
qui comporte la liste de sélection et sa légende. On va donc
relancer la transformation XSLT sur l'ensemble du fichier XML mais
avec un fragment seulement (zoneSelection)
de la feuille de style XSLT, ce fragment étant bien entendu celui
qui sert à générer le fieldset, et qui est donc lui
aussi identifié par le même attribut ID (selection).
Au chargement initial de la page XML, on aura donc pris soin d'identifier
ce fragment de feuille de style :
zoneSelection=stylesheet.selectSingleNode('//*[@ID="selection"]');
Note. Dans notre exemple une complication supplémentaire
est apportée par le fait que le fragment de feuille de style en
question fait appel à un script, lui-même contenu dans l'élément <xsl:script>
de la feuille de style. Il était donc nécessaire d'ajouter
cet élément à notre fragment de feuille de style.
Cet élément a donc été lui-aussi identifié
au chargement initial de la page XML :
zoneScript=stylesheet.selectSingleNode('//xsl:script');
Et le fragment de feuille de style a été constitué
en définitive de l'ensemble de ces deux éléments
:
zoneSelection.appendChild(zoneScript);
function selectfeature() {
francais=document.forms(0).langue.checked;
listetheme=document.forms(0).themes;
listecategorie=document.forms(0).categories;
// Fabriquer l'expression XPath (pattern)
pattern = "//"
if (listetheme.value!="") {
pattern = "//categorie["+listetheme.value+"]//"
}
if (listecategorie.value!="") {
pattern+="categorie["+listecategorie.value+"]//"
}
pattern+="document";
if (francais) {
pattern+="[@lang='fr']"
}
selectfeatureField.value=pattern;
nombreDocs='selectNodes("'+pattern+'").length';
compteurField.text="convertir("+nombreDocs+")";
plurielField.value=nombreDocs+' > 1';
uniqueField.value=nombreDocs+' == 1';
document.forms(0).selection.outerHTML=
source.documentElement.transformNode(zoneSelection);
}
Voir/télécharger les fichiers de l'exemple
:
Contribuez ! Faites-nous profiter
de vos scénarios les plus significatifs.
Retour à la page d'accueil.