Recipe11.4.Creating Interactive SVG-Enabled Web Pages


Recipe 11.4. Creating Interactive SVG-Enabled Web Pages

Problem

You want to embed SVG in HTML to create an interactive user experience.

Solution

This solution is based on an adaptation of code presented in Didier Martin's XML.com article Integration by Parts: XSLT, XLink and SVG (http://www.xml.com/lpt/a/2000/03/22/style/index.html). The stylesheet embeds an SVG graphic in an HTML page along with information obtained from an XML document. JavaScript is added to allow the user to interact with the graphic. This particular example is a prototype of an online real-estate application in which users can interact with a layout diagram of a house.

The input XML contains information about the house. Each room is associated with an id that links the data in the room to a g element in the SVG diagram with the same identifier:

<?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 >       <Name>Bedroom</Name>       <Length>10</Length>       <Width>10</Width>       <Windows>2</Windows>       <Misc>View of junk yard</Misc>     </Room>     <Room >       <Name>Bedroom</Name>       <Length>10</Length>       <Width>10</Width>       <Windows>1</Windows>       <Misc>Elvis slept here</Misc>     </Room>     <Room >       <Name>Master Bedroom</Name>       <Length>18</Length>       <Width>10</Width>       <Windows>3</Windows>       <Misc>Walk-in Closet</Misc>     </Room>     <Room >       <Name>Master Bath</Name>       <Length>5</Length>       <Width>5</Width>       <Windows>1</Windows>       <Misc>Full Bath w/ bidet</Misc>     </Room>     <Room >       <Name>Kitchen</Name>       <Length>20</Length>       <Width>18</Width>       <Windows>2</Windows>       <Misc>New Cabinets</Misc>     </Room>     <Room >       <Name>Living Room</Name>       <Length>18</Length>       <Width>18</Width>       <Windows>2</Windows>       <Misc>View of Rose Garden</Misc>     </Room>     <Room >       <Name>Bathroom</Name>       <Length>6</Length>       <Width>5</Width>       <Windows>1</Windows>       <Misc>Heart-Shaped Tub</Misc>     </Room>   </Layout> </House>

The stylesheet embeds the SVG file, converts the XML data into a table, and adds canned JavaScript that makes the page interactive, as shown in Figure 11-18.

Figure 11-18. Interactive SVG generated from 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;     // When the mouse pointer triggers the mouse over event // This function is called. // We are using both the SVGDOM and the XML DOM // to access the document's tree nodes. // More particularly, this function change elements // identified by the id attribute. // Note that to change a style attribute with the SVG DOM does not // require to know in advance the value of the style attribute. // In contrast, with the XML DOM, you need to know the full content // of the style attribute. function on_mouse_over (ID) {      if (ID == item_selected)           return true;          var obj_name = ID ;            // Change the SVG element's style      // -------------------------------      // 1 - get the SVGDOM document element from the Adobe SVG viewer      // 2 - Then, get the element included in the SVG document and which is      // referred by the id identifier.      // 3 - Finally, get the style attribute from the SVG DOM element node.      // The getStyle function is particular to the SVG DOM.      // The get style function returns a style object.      // We change the 'fill' style attribute with the returned      // style object. Note that in contrast to the XML DOM,      // we do not need to know in advance the content of the      // style attribute's value to change one of the CSS attribute.      var svgdoc = document.figure.getSVGDocument( );      var svgobj = svgdoc.getElementById(obj_name);      if (svgobj != null)      {           var svgStyle = svgobj.getStyle( );            svgStyle.setProperty ('fill', 'yellow');       }          // Here is what we should have if the target browser      // would fully support the XML DOM      // --------------------------------------------------      // Get the element included in this HTML document (see in the body      // section) and which is referred by the identifier.      ///Change the element's style attribute using the      // XML DOM. Please note that in contrast to the SVG DOM      // function, the whole style attribute's value is changed and      // not the value of a single contained CSS attribute.      // DOES NOT WORK...      var svgdesc = document.getElementById(obj_name);      if (svgdesc != null)           svgdesc.setAttribute("style", "background-color:yellow; cursor:hand");          // Here is what we do for the IE 5 DHTML DOM      // -----------------------------------------      var DHTMLobj = document.all.item(obj_name)      if (DHTMLobj != null)           DHTMLobj.style.backgroundColor = "yellow";      return true; }     // When the mouse pointer triggers the mouse over event // This function is called. // We are using both the SVGDOM and the XML DOM // to access the document's tree nodes. // More particularly, this function change elements // identified by the id attribute. // Note that to change a style attribute with the SVG DOM does not // require to know in advance the value of the style attribute. // In contrast, with the XML DOM, you need to know the full content // of the style attribute. function on_mouse_out (ID) {      if (ID =  = item_selected)           return true;          var obj_name = ID ;            // Change the SVG element's style      // -------------------------------      // 1 - get the SVGDOM document element from the Adobe SVG viewer      // 2 - Then, get the element included in the SVG document and which is      // referred by the identifier.      // 3 - Finally, get the style attribute from the SVG DOM element node.      // The getStyle function is particular to the SVG DOM.      // The get style function returns a style object.      // We change the 'fill' style attribute with the returned      // style object. Note that in contrast to the XML DOM,      // we do not need to know in advance the content of the      // style attribute's value to change one of the CSS attribute.      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');       }          // Here is what we should have if the target browser      // would fully support the XML DOM      // --------------------------------------------------      // Get the element included in this HTML document (see in the body      // section) and which is referred by the identifier.       ///Change the element's style attribute using the      // XML DOM. Please note that in contrast to the SVG DOM      // function, the whole style attribute's value is changed and      // not the value of a single contained CSS attribute.      // DOES NOT WORK...      var svgdesc = document.getElementById(obj_name);      if (svgdesc != null)           svgdesc.setAttribute("style", "background-color:white;");          // Here is what we do for the 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 ;            // reset the color of the previously selected room      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";           }      }      // Now select the new room      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  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>

Discussion

Prior examples focused on generating SVG from XML, while this one focuses on integrating SVG into a larger application based on other web technologies. This recipe only touches on the potential of such applications. SVG contains facilities for animation and dynamic content that, when coupled with XSLT's transformation capabilities, can lead to some impressive results. Consider the following stylesheet that is based on the graph-drawing primitives of Recipe 11.2, but allows the user to interact with the graph:

<?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         class/>     <xsl:processing-instruction name="import">       <xsl:text>namespace="svg" implementation="#AdobeSVG"</xsl:text>     </xsl:processing-instruction> <script><![CDATA[     function on_change (ID,VALUE) {     //Get the svg doc      var svgDocument = document.all.item('figure').getSVGDocument( );            //The bars id is prefixed with the context value + _bar_ + ID      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" >         <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>                   <!-- Bars oriented upward are rotated so the ids need                    <!-- to be reversed. See svgu:bars implementation -->                   <!-- for clarification. -->                   <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>

This stylesheet results is a web page that allows you to change data while the height of the bars responds in kind. This stylesheet also demonstrates the technique for inlining SVG content in HTML. Unfortunately, it works only with IE 5.5 or higher browsers and assumes that you use the Adobe SVG plug-in.[3]

[3] This, of course, is the configuration used by a large segment of the modern world. A future version of Firefox (possibly 1.1) will have native SVG support.

See Also

Didier Martin's XML.com article Integration by Parts: XSLT, XLink and SVG (http://www.xml.com/lpt/a/2000/03/22/style/index.html) contains a more compelling example involving interaction with a CAD diagram of a complex part.

J. David Eisenberg's SVG Essentials (O'Reilly, 2002) contains detailed information about SVG animation and scripting.

Developers who know Java and want to do some serious SVG development should check out Apache Batik (http://xml.apache.org/batik/).




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net