Werte für id-Attribute mit XSLT generieren
XSLT enthält die Funktion generate-id(), mit der man eindeutige IDs für Elemente generieren kann. Bei umfangreichen Dokumenten erspart man sich die Aufgabe, eindeutige IDs manuell vergeben zu müssen. In (X)HTML besitzen die meisten Elemente das Attribut "id", dessen eindeutiger Wert dazu dient, HTML-Elemente zu identifizieren. Per JavaScript kann man beispielsweise per getElementById('Beispiel') auf genau das Element zugreifen, dessen ID-Attribut den Wert 'Beispiel' enthält.
Die Funktion generate-id() gibt für den selben Knoten im XML-Dokument immer den selben String als Schlüssel zur Laufzeit zurück. Dadurch kann man die ID eines Knotens gleich mehrfach durch Aufrufen der Funktion mit dem entsprechenden Parameter auslesen.
Aufruf der Funktion
Man ruft die Funktion generate-id() in der Regel mit einem Nodeset als Parameter auf. Wird die Funktion ohne Parameter aufgerufen, wird standardmäßig der Kontextknoten ausgewählt. Falls die angegebene Knotenmenge leer ist, wird ein leerer String zurückgegeben. Dadurch kann man leicht erkennen, dass etwas schief gelaufen ist.
Das Standard-Beispiel für den Einsatz von generate-id(), das man häufig in Tutorials und einschlägigen Büchern findet, ist die Erzeugung eines Inhaltsverzeichnisses. Dabei wird ein Quelldokument, beispielsweise ein Buch, in ein navigierbares HTML-Dokument transformiert, das ein Inhaltsverzeichnis enthält. Die Einträge im Inhaltsverzeichnis sind dabei mit den jeweiligen Titeln im Fließext verlinkt.
Das Quelldokument könnte dabei so aussehen:
<book>
<title>foo baz</title>
<chapter>
<title>Introduction</title>
<para>This is a boring book.</para>
</chapter>
<chapter>
<title>Boring chapter</title>
<para>This is a boring chapter.</para>
</chapter>
</book>
Das XSLT-Template, mit dem dieses Dokument in eine navigierbare HTML-Datei umgewandelt werden soll, könnte so implementiert werden:
...
<xsl:template match="book">
<h1><xsl:value-of select="title"/></h1>
<ul class="toc">
<xsl:for-each select="chapter/title">
<!-- Aufruf von generate-id() mit Kontextknoten -->
<li>
<a href="#{generate-id()}">
<xsl:value-of select="text()"/>
</a><br />
</li>
</xsl:for-each>
</ul>
...
<!-- Ausgabe des Inhalts -->
<xsl:for-each select="chapter">
<!-- Aufruf von generate-id() mit Knotenangabe als Parameter -->
<a name="{generate-id(title)}">
<xsl:value-of select="title/text()"/>
</a>
<!-- An dieser Stelle erfolgt die Ausgabe der <para>-Elemente -->
</xsl:for-each>
</xsl:template>
...
Mit<xsl:for-each> werden beim ersten Mal alle <title>-Elemente durchiteriert und der Textknoten dieser Elemente zwischen Ankertags gesetzt. Um den Wert für das jeweilige href-Attribut zu gewinnen, wird die Funktion generate-id() mit leerem Parameter aufgerufen.Da es sich beim Kontextknoten um <title> handelt, berechnet generate-id() die id für das aktuelle <title>-Element.
Beim zweiten <xsl:for-each> werden alle <chapter>-Elemente durchiteriert, da man sowohl die Titel als auch die entsprechenden <para>-Elemente für die Verarbeitung benötigt. Die Titel werden dabei zwischen <a>-Elemente gesetzt, die ein name-Attribut enthalten. Die ID des <title>-Elementes wird dabei wieder mit der Funktion generate-id() ausgelesen, diesmal jedoch mit der Angabe der Knotenmenge, für die die id ausgelesen werden soll ("title"). Die Funktion generate-id() gibt dabei für den selben title-Knoten den selben id-String zurück wie beim ersten Durchlauf, sodass der Anker im Inhaltsverzeichnis auf den Titel im Fließtext verweist.
Ergebnis
Das Zieldokument sieht nach der Transformation wie folgt aus (auch hier wird nur ein Ausschnitt dargestellt):
<html>
<head>
...
</head>
<body>
<h1>foo baz</h1>
<ul class="toc">
<li>
<a href="#145536">Introduction</a>
</li>
<li>
<a href="#554432">Boring chapter</a>
</li>
</ul>
...
<h2><a name="145536">Introduction</a></h2>
<p>This is a boring book.</p>
<p>...</p>
<h2><a name="554432">Boring chapter</a></h2>
<p>This is a boring chapter.</p>
<p>...</p>
</body>
</html>
Posted in XSLT