Interaktive SVG-basierte Webseiten erzeugen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie möchten SVG in HTML einbetten, mit dem der Benutzer interaktiv agieren kann.

Lösung

Diese Lösung basiert auf einer abgewandelten Version eines Codes, der in einem Artikel von Didier Martin, Integration by Parts: XSLT, XLink and SVG, enthalten ist. Das Stylesheet bettet eine SVG-Grafik in eine HTML-Seite zusätzlich zu Angaben ein, die aus einem XML-Dokument kommen. JavaScript wird hinzugefügt, damit der Benutzer mit der Grafik interagieren kann. Dieses spezielle Beispiel ist ein Prototyp einer Online-Makleranwendung, in der die Benutzer mit dem Grundrissplan eines Hauses interagieren können.

Die XML-Eingabe enthält Information über das Haus. Jedem Raum ist eine id zugeordnet, die eine Verbindung von den Daten dieses Raums zu einem g-Element im SVG-Diagramm mit dem gleichen Bezeichner herstellt:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="HouseLayout.xsl"?>
<House>
  <Location>
    <Address>1234 Main St. </Address>
    <City>Pleasantville </City>
    <State>NJ</State>
  </Location>
  <Layout figure="HouseLayout.svg">
    <Room id="bedroom1">
      <Name>Bedroom</Name>
      <Length>10</Length>
      <Width>10</Width>
      <Windows>2</Windows>
      <Misc>View of junk yard</Misc>
    </Room>
    <Room id="bedroom2">
      <Name>Bedroom</Name>
      <Length>10</Length>
      <Width>10</Width>
      <Windows>1</Windows>
      <Misc>Elvis slept here</Misc>
    </Room>
    <Room id="masterBedroom">
      <Name>Master Bedroom</Name>
      <Length>18</Length>
      <Width>10</Width>
      <Windows>3</Windows>
      <Misc>Walk-in Closet</Misc>
    </Room>
    <Room id="masterBath">
      <Name>Master Bath</Name>
      <Length>5</Length>
      <Width>5</Width>
      <Windows>1</Windows>
      <Misc>Full Bath w/ bidet</Misc>
    </Room>
    <Room id="kitchen">
      <Name>Kitchen</Name>
      <Length>20</Length>
      <Width>18</Width>
      <Windows>2</Windows>
      <Misc>New Cabinets</Misc>
    </Room>
    <Room id="livingRoom">
      <Name>Living Room</Name>
      <Length>18</Length>
      <Width>18</Width>
      <Windows>2</Windows>
      <Misc>View of Rose Garden</Misc>
    </Room>
    <Room id="bath1">
      <Name>Bathroom</Name>
      <Length>6</Length>
      <Width>5</Width>
      <Windows>1</Windows>
      <Misc>Heart-Shaped Tub</Misc>
    </Room>
  </Layout>
</House>

Das Stylesheet bettet die SVG-Datei ein, konvertiert die XML-Daten in eine Tabelle und fügt JavaScript aus der Dose hinzu, um die Seite interaktiv zu machen (siehe folgende Abbildung).

Interaktives SVG, erzeugt aus XML

Abbildung: Interaktives SVG, erzeugt aus XML.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0">
  <xsl:output method="html" version="4"/>
  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="concat(*/*/Address,*/*/City,*/*/State)"/></title>
        <script><![CDATA[

        var item_selected = null;

        // Wenn der Mauszeiger das Ereignis "mouse over" auslöst, wird diese Funktion aufgerufen.
        // Wir benutzen sowohl SVG-DOM als auch XML-DOM, um auf die Baumknoten des Dokuments zuzugreifen.
        // Insbesondere ändert diese Funktion Elemente, die mit dem id-Attribut angegeben werden.
        // Beachten Sie, dass es zum Ändern eines Stilattributs mit SVG-DOM nicht nötig ist, vorab den Wert des Stilattributs zu kennen.
        // Anders bei XML DOM: Hier müssen Sie den ganzen Inhalt des Stilattributs kennen.
       function on_mouse_over (ID)
       {
            if (ID == item_selected)
                 return true;

            var obj_name = ID ;

            // Ändern des Stils des SVG-Elements
            // ---------------------------------
            // 1 - Hole das SVG-DOM-Dokumentelement aus dem Adobe-SVG-Viewer,
            // 2 - hole dann das Element in dem SVG-Dokument, auf das der id-Bezeichner verweist,
            // 3 - und hole schließlich das Stilattribut aus dem SVG-DOM-Elementknoten.
            // Die Funktion getStyle gehört zum SVG-DOM und gibt ein Stilobjekt zurück.
            // Wir ändern das Stilattribut 'fill' auf das zurückgegebene Stilobjekt. 
            // Beachten Sie, dass wir anders als bei XML-DOM den Wert des Stilattributs nicht vorab kennen müssen, um ein CSS-Attribut zu ändern.
            var svgdoc = document.figure.getSVGDocument( );
            var svgobj = svgdoc.getElementById(obj_name);
            if (svgobj != null)
            {
                var svgStyle = svgobj.getStyle( );
                svgStyle.setProperty ('fill', 'yellow');
             }
            // Folgendes sollten wir machen, wenn der Zielbrowser XML DOM vollständig unterstützen würde:
            // --------------------------------------------------
            // Hole das Element in diesem HTML-Dokument (im body nachsehen), auf das der id-Bezeichner verweist.
            // Ändere das Stilattribut des Elements mit XML DOM.
            // Beachten Sie, dass im Gegensatz zur SVG DOM-Funktion der ganze Wert des Stilattributs geändert wird und nicht der Wert eines einzigen darin enthaltenen CSS-Attributs.
            // FUNKTIONIERT NICHT...
            var svgdesc = document.getElementById(obj_name);
            if (svgdesc != null)
                  svgdesc.setAttribute("style", "background-color:yellow; cursor:hand");

            // Folgendes machen wir beim IE 5 DHTML DOM:
            // -----------------------------------------
            var DHTMLobj = document.all.item(obj_name)
            if (DHTMLobj != null)
                  DHTMLobj.style.backgroundColor = "yellow";
            return true;
       }

       // Wenn der Mauszeiger das Ereignis "mouse out" auslöst, wird diese Funktion aufgerufen.
       // Wir benutzen sowohl SVG-DOM als auch XML DOM, um auf die Baumknoten des Dokuments zuzugreifen.
       // Insbesondere ändert diese Funktion Elemente, die mit dem id-Attribut angegeben werden.
       // Beachten Sie, dass es zum Ändern eines Stilattributs mit SVG-DOM nicht nötig ist, vorab den Wert des Stilattributs zu kennen.
       // Anders bei XML DOM: Hier müssen Sie den ganzen Inhalt des Stilattributs kennen.
       function on_mouse_out (ID)
       {
           if (ID =  = item_selected)
                 return true;

           var obj_name = ID ;

           // Den Stil des SVG-Elements ändern
           // ---------------------------------
           // 1 - Hole das SVGDOM-Dokumentelement aus dem Adobe-SVG-Viewer,
           // 2 - hole dann das Element in dem SVG-Dokument, auf das der id-Bezeichner verweist,
           // 3 - und hole schließlich das Stilattribut aus dem SVG-DOM-Elementknoten.
           // Die Funktion getStyle gehört zum SVG-DOM und gibt ein Stilobjekt zurück.
           // Wir ändern das Stilattribut 'fill' auf das zurückgegebene Stilobjekt.
           // Beachten Sie, dass wir anders als bei XML DOM den Wert des Stilattributs nicht vorab kennen müssen, um ein CSS-Attribut zu ändern.
           var svgdoc = document.figure.getSVGDocument( );
           var svgobj = svgdoc.getElementById(obj_name);
           if (svgobj != null)
           {
                var svgStyle = svgobj.getStyle( );
                svgStyle.setProperty ('fill', 'white');
                svgStyle.setProperty ('stroke', 'white');
           }

           // Folgendes sollten wir machen, wenn der Zielbrowser XML DOM vollständig unterstützen würde:
           // --------------------------------------------------
           // Hole das Element in diesem HTML-Dokument (im body nachsehen), auf das der id-Bezeichner verweist.
           // Ändere das Stilattribut des Elements mit XML DOM.
           // Beachten Sie, dass im Gegensatz zur SVG DOM-Funktion der ganze Wert des Stilattributs geändert wird und nicht der Wert eines einzigen darin enthaltenen CSS-Attributs.
           // FUNKTIONIERT NICHT...
           var svgdesc = document.getElementById(obj_name);
           if (svgdesc != null)
                 svgdesc.setAttribute("style", "background-color:white;");

           // Folgendes machen wir beim IE 5 DHTML DOM:
           // ----------------------------------------
           var DHTMLobj = document.all.item(obj_name)
           if (DHTMLobj != null)
                 DHTMLobj.style.backgroundColor = "white";

           return true;
      }

      function on_mouse_click(ID)
      {
           var obj_name = ID ;

           // setze die Farbe des zuvor gewählten Raums zurück
           if (item_selected)
           {
                var svgdoc = document.figure.getSVGDocument( );
                var svgobj = svgdoc.getElementById(obj_name);
                if (svgobj != null)
                {
                      var svgStyle = svgobj.getStyle( );
                      svgStyle.setProperty ('fill', 'white');
                }
                var DHTMLobj = document.all.item(obj_name)
                if (DHTMLobj != null)
                {
                      DHTMLobj.style.backgroundColor = "white";
                      DHTMLobj.style.fontWeight  = "normal";
                }
           }
           // wähle nun den neuen Raum
           if (item_selected != ID)
           {
               var svgdoc = document.figure.getSVGDocument( );
               var svgobj = svgdoc.getElementById(obj_name);
               if (svgobj != null)
               {
                      var svgStyle = svgobj.getStyle( );
                      svgStyle.setProperty ('fill', '#C0C0C0');
               }
               var DHTMLobj = document.all.item(obj_name)
               if (DHTMLobj != null)
               {
                     DHTMLobj.style.backgroundColor = "#C0C0C0";
                     DHTMLobj.style.fontWeight  = "bolder";
               }
               item_selected = ID;
           }
           else
               item_selected = null;


           return true;
      }
     ]]></script>
      </head>
      <body>
       <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="Layout">
    <div align="center">
      <embed name="figure" width="540" height="540" type="image/svg" pluginspage="http://www.adobe.com/svg/viewer/install/">
        <xsl:attribute name="src"><xsl:value-of select="@figure"/></xsl:attribute>
      </embed>
    </div>
    <table border="0" cellpadding="1" cellspacing="0" width="100%" bgcolor="black">
      <tr>
        <table border="0" cellpadding="5" cellspacing="0" width="100%" bgcolor="white">
          <tr style="background-color:#990033; color:white;">
            <td>Room</td>
            <td align="right">Length</td>
            <td align="right">Width</td>
            <td align="right">Windows</td>
            <td>Miscelaneous</td>
          </tr>
          <xsl:apply-templates/>
        </table>
      </tr>
    </table>
  </xsl:template>
  <xsl:template match="Room">
    <tr id="{@id}" style="'background-color:white;'" onmouseover="on_mouse_over('{@id}')" onmouseout="on_mouse_out('{@id}')" onclick="on_mouse_click('{@id}')">
      <td><xsl:value-of select="Name"/></td>
      <td align="right"><xsl:value-of select="Length"/></td>
      <td align="right"><xsl:value-of select="Width"/></td>
      <td align="right"><xsl:value-of select="Windows"/></td>
      <td><xsl:value-of select="Misc"/></td>
    </tr>
  </xsl:template>
  <xsl:template match="text( )"/>
</xsl:stylesheet>

Diskussion

Die bisherigen Beispiele konzentrierten sich darauf, SVG aus XML zu erzeugen, während es in diesem um die Integration von SVG in eine größere Anwendung auf Basis anderer Webtechnologien geht. In diesem Rezept kann das Potenzial solcher Anwendungen nur gestreift werden. SVG bietet Möglichkeiten für Animation und dynamische Inhalte, die in Kombination mit den Transformationsmöglichkeiten von XSLT zu sehr beeindruckenden Ergebnissen führen können. Betrachten Sie das folgende Stylesheet, das auf den Primitiven zum Zeichnen von Diagrammen aus dem Rezept Wiederverwendbare SVG-Hilfswerkzeuge für Diagramme erzeugen aufbaut, es dem Benutzer aber ermöglicht, mit dem Diagramm zu interagieren:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:svgu="http://www.ora.com/XSLTCookbook/ns/svg-utils" xmlns:test="http://www.ora.com/XSLTCookbook/ns/test" exclude-result-prefixes="svgu test">
  <xsl:import href="svg-utils.xslt"/>
  <xsl:output method="html"/>
  <test:data>1.0</test:data>
  <test:data>2.0</test:data>
  <test:data>3.0</test:data>
  <test:data>4.0</test:data>
  <test:data>5.0</test:data>
  <test:data>13.0</test:data>
  <test:data>2.7</test:data>
  <test:data>13.9</test:data>
  <test:data>22.0</test:data>
  <test:data>8.5</test:data>
  <xsl:template match="/">
    <html>
      <head>
        <title>Interactive Bar Chart</title>
        <object id="AdobeSVG" classid="clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2"/>
        <xsl:processing-instruction name="import">
          <xsl:text>namespace="svg" implementation="#AdobeSVG"</xsl:text>
        </xsl:processing-instruction>
        <script><![CDATA[

        function on_change (ID,VALUE)
        {
            // Hole SVG-Dokument
            var svgDocument = document.all.item('figure').getSVGDocument( );

            // Der bars-id wird der Kontextwert + _bar_ + ID vorangesetzt
            var barName = "interact_bar_" + ID ;

            var barObj = svgDocument.getElementById(barName);
            if (barObj != null)
            {
               barObj.setAttribute('y2', VALUE);
            }

            return true;
        }

    ]]></script>
      </head>
      <body>
        <div align="center">
          <svg:svg width="400" height="400" id="figure">
            <xsl:call-template name="svgu:bars">
              <xsl:with-param name="data" select="document('')/*/test:data"/>
              <xsl:with-param name="width" select=" '300' "/>
              <xsl:with-param name="height" select=" '350' "/>
              <xsl:with-param name="offsetX" select=" '50' "/>
              <xsl:with-param name="offsetY" select=" '25' "/>
              <xsl:with-param name="boundingBox" select="1"/>
              <xsl:with-param name="max" select="25"/>
              <xsl:with-param name="context" select=" 'interact' "/>
            </xsl:call-template>
          </svg:svg>
        </div>
        <table border="1" cellspacing="1" cellpadding="1">
          <tbody>
            <xsl:for-each select="document('')/*/test:data">
              <xsl:variable name="pos" select="position( )"/>
              <xsl:variable name="last" select="last( )"/>
              <tr>
                <td>Bar <xsl:value-of select="$pos"/></td>
                <td>
                  <input type="text">
                    <xsl:attribute name="value">
                      <xsl:value-of select="."/>
                    </xsl:attribute>
                    <xsl:attribute name="onchange">
                      <xsl:text>on_change(</xsl:text>
                      <!-- Vertikal ausgerichtete Balken werden gedreht, d.h., die IDs müssen umgekehrt werden. Siehe die Implementierung von svgu:bars als Erklärung. -->
                      <xsl:value-of select="$last - $pos + 1"/>
                      <xsl:text>, this.value)</xsl:text>
                    </xsl:attribute>
                  </input>
                </td>
              </tr>
            </xsl:for-each>
          </tbody>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Das Ergebnis dieses Stylesheets ist eine Webseite, bei der Sie die Daten ändern können, während sich die Balkenhöhe sofort anpasst. Außerdem demonstriert dieses Stylesheet eine Technik, mit der Sie SVG-Inhalte inline in HTML verwenden können. Leider funktioniert das nur mit IE-Browsern in der Version 5.5 oder höher und setzt voraus, dass Sie das Adobe SVG-Plug-in verwenden. (Das ist nun einmal, was in einem großen Teil der modernen Welt immer noch benutzt wird. In der aktuellen Version von Firefox (1.5) ist eine SVG-Unterstützung bereits eingebaut.)

Siehe auch

Didier Martins Artikel Integration by Parts: XSLT, XLink and SVG (siehe Link ganz oben). Er enthält ein noch überzeugenderes Beispiel, bei dem es um die Interaktion mit dem CAD-Diagramm eines komplizierten Bauteils geht.

J. David Eisenbergs Buch SVG Essentials (O'Reilly, 2002) enthält detaillierte Angaben über SVG-Animation und -Scripting.

Entwickler, die sich mit Java auskennen und sich ernsthaft mit SVG beschäftigen, sollten einen Blick auf Apache Batik werfen.

  

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de