Accueil

Comment faire pour...

Afficher plein écran

Réaliser un catalogue interactif (MSIE5 "natif")

Emmanuel Lazinier
 
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 &amp; 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 :

  1. par navigation directe dans le catalogue au moyen d'arborescences dépliables et repliables ;
  2. 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 :
  1. les éventuelles sous-catégories de la catégorie (<xsl:apply-templates select="categorie"> ) et/ou
  2. 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 : 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 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.