Navigation

All applications, whether they are large enterprise applications or programs for hand-held devices, have a navigation challenge. There are many questions to consider when designing a navigation system. Should the navigation be function based or data based? Should trees be used? Menus? Menus and trees? Toolbars? Buttonbars? The answer to these questions is (of course),"It depends." The navigation tools your application provides and the way in which it organizes them depends on factors such as the application's purpose, breadth, and user base. The bottom line is that the way users navigate through the system is less important than the speed at which they are able to do it.

Although implementations may vary, most navigation systems have similar abstract characteristics. All navigation systems are merely organized collections of links. They might contain links to forms or links to Web pages. They might be organized by the type of function they perform or by the type of data on which they perform it. Therefore, while XML might not be able to tell you how to solve your program's navigation challenge, using it to model your application's navigation scheme can provide several key benefits.

Figure 6-5 Using an XML List construct to display CD Tracks.

  • XML helps you think about navigation in more abstract terms. This frees your mind to think outside the box.
  • It promotes modularity in your application. The business logic that builds the navigation document should not be cluttered with phrases like If browserType = `netscape' then . . .
  • It makes your UI more extensible. Newer navigation UI schemes can be plugged into the existing navigation structure with no modification to your business logic.

The best way to begin thinking about how XML can help is by looking at a sample navigation schema. In this section we will examine how to model a navigation system useing XML. We will then attempt to transform our navigation model into a format suitable for rendering on a Web browser.

Try to think outside the box when incorporating XML into your application. Why not transform an XML navigation tree into a voice-activated UI?

A Simple Navigation Schema

This schema looks similar to the Noverant tree scheme discussed in Chapter 5, and with good reason. A tree is a common navigation component. The heart of the more generic navigation schema is the <navitem> element. Each <navitem> is a link to a document in a Web-based application. A <navitem> contains a description, one or more icons, a hyperlink reference, and a recursive collection of children navitems. This schema is simple yet flexible and offers an infinite variety of ways to model a navigation system.

 <ElementType name="navitem.description" content="textOnly" model="closed"/> <ElementType name="navitem.openIcon" content="textOnly" model="closed"/> <ElementType name="navitem.closedIcon" content="textOnly" model="closed"/> <ElementType name="navitem.icon" content="textOnly" model="closed"/> <ElementType name="href" order="many" content="textOnly" model="closed"/> <ElementType name="navitem.children" order="many" content="eltOnly" model="closed"> <element type="navitem" minOccurs="0" maxOccurs="*"/> </ElementType> <ElementType name="navitem" order="many" content="eltOnly" model="closed"> <element type="navitem.description" minOccurs="1" maxOccurs="1"/> <element type="navitem.icon" minOccurs="0" maxOccurs="1"/> <element type="navitem.openIcon" minOccurs="0" maxOccurs="1"/> <element type="navitem.closedIcon" minOccurs="0" maxOccurs="1"/> <element type="href" minOccurs="0" maxOccurs="0"/> <element type="navitem.children" minOccurs="0" maxOccurs="1"/> </ElementType> 

The <navigation> element is a special <navitem> in that it anchors the navigation hierarchy.

 <ElementType name="navigation" order="many" content="eltOnly" model="closed"> <AttributeType name="type" /> <element type="navigation.description" minOccurs="1" maxOccurs="1"/> <element type="navigation.icon" minOccurs="0" maxOccurs="1"/> <element type="navigation.openIcon" minOccurs="0" maxOccurs="1"/> <element type="navigation.closedIcon" minOccurs="1" maxOccurs="1"/> <element type="href" minOccurs="0" maxOccurs="1"/> <element type="navitem.children" minOccurs="0" maxOccurs="1"/> </ElementType>  

A Navigation Example

Now let's put the schema from the previous section to use for Noverant. The following navigation XML document is a snippet from Noverant's back-office application. The example includes entries for maintaining the CD, artist, and inventory databases.

 <navigation> <description>Noverant Back-Office Root Menu</description> <children> <navitem> <description>Artists</description> <icon>artist.gif</icon> <children> <navitem> <description>Search for an Artist</description> <href>controller.asp?view=artistSearch</href> <icon>artist.gif</icon> </navitem> <navitem> <description>New Artist</description> <href>controller.asp?view=artistAdd</href> <icon>newArtist.gif</icon> </navitem> </children> </navitem> <navitem> <description>CDs</description> <icon>cd.gif</icon> <children> <navitem> <description>Search for a CD</description> <href>controller.asp?view=cdSearch</href> <icon>cd.gif</icon> </navitem> <navitem> <description>New CD</description> <href>controller.asp?view=cdAdd</href> <icon>newCD.gif</icon> </navitem> </children> </navitem> <navitem> <description>Inventory</description> <icon>inventory.gif</icon> <children> <navitem> <description>View Current Inventory</description> <href>controller.asp?view=inventory</href> <icon>inventory.gif</icon> </navitem> <navitem> <description>View Low-Inventory Items</description> <href>controller.asp?view=lowinventory</href> <icon>lowinventory.gif</icon> </navitem> <navitem> <description>Place Supply Requisition</description> <href>controller.asp?view=requisition</href> <icon>requisition.gif</icon> </navitem> </children> </navitem> </children> </navigation> 

Note that not all of the <navitem> elements are completely filled out. The code is still valid according to our schema, because only the <description> element is required. Why have a <navitem> element with no hyperlink? The answer is that not all items that appear in a menu system or table-of-contents tree provide links to documents. Some might serve as placeholders to group children elements.

Think about the preceding XML document and what it represents. More importantly, think about what it does not represent. Nothing in the document indicates how the information should be rendered. The navigation elements can be displayed as a tree, drop-down menu, or tab menu just as easily. Let's explore this example further and create two different XSLT implementations for our navigation document.

Creating a Tab Menu

The first implementation will be a two-tiered tab approach. The presentation rules will assume a two-level navigation document (that is, a list of categories and options) and will build a set of tabs that, when clicked, reveal a list of options from which the user can choose. The following XSLT code will be added to the other template rules we have defined thus far.

 <xsl:template match="navigation[@type='menu']"> <html> <head> <xsl:text disable-output-escaping = "yes"> <![CDATA[ <link rel="stylesheet" type="text/css" href="css/style.css"/> ]]> </xsl:text> <style type="text/css"> body{background-color:C0C0C0} </style> <script type="text/javascript"> 

The following JavaScript functions handle all user interaction with the tab folders. The clearFunctions( ) function clears out the present set of options whenever a new tab is clicked. The clearModuleStyles( ) deemphasizes the inactive tabs. Finally the showFunction( ) function highlights the chosen tab and builds a new list of options.

 <![CDATA[ function clearFunctions() { var i = 1; var theFunction = ""; var isFunction = true; while (isFunction) { theFunction = "function"+i; var hideDiv = document.getElementById(theFunction); if (hideDiv) { hideDiv.style.visibility = "hidden"; i++; } else { isFunction = false; } } } function clearModuleStyles() { var i = 1; var theModule = ""; var isModule = true; while (isModule) { theModule = "module"+i; theModuleLeft = "moduleLeft"+i; theModuleRight = "moduleRight"+i; theModuleAnchor = "moduleAnchor"+i; cleanStyle = document.getElementById(theModule); cleanStyleLeft = document.getElementById(theModuleLeft); cleanStyleRight = document.getElementById(theModuleRight); cleanStyleAnchor = document.getElementById(theModuleAnchor); if (cleanStyle) { cleanStyle.style.backgroundColor = 'steelblue'; cleanStyle.style.borderBottom = '1px solid'; cleanStyle.style.borderColor = '336699'; cleanStyleLeft.style.backgroundColor = 'steelblue'; cleanStyleLeft.style.borderBottom = '1px solid'; cleanStyleLeft.style.borderColor = '336699'; cleanStyleRight.style.backgroundColor = 'steelblue'; cleanStyleRight.style.borderBottom = '1px solid'; cleanStyleRight.style.borderColor = '336699'; cleanStyleAnchor.style.color = 'white'; i++; } else { isModule = false; } } } function showFunction(id) { clearFunctions(); clearModuleStyles(); theId = "function"+id; thisFunction = document.getElementById(theId); thisFunction.style.visibility = 'visible'; theId = "module"+id; theLeftId = "moduleLeft"+id; theRightId = "moduleRight"+id; theAnchorId = "moduleAnchor"+id; thisModule = document.getElementById(theId); thisModuleRight = document.getElementById(theRightId); thisModuleLeft = document.getElementById(theLeftId); thisModuleAnchor = document.getElementById(theAnchorId); thisModule.style.backgroundColor = 'white'; thisModule.style.borderBottom = '0'; thisModuleRight.style.backgroundColor = 'white'; thisModuleRight.style.borderBottom = '0'; thisModuleLeft.style.backgroundColor = 'white'; thisModuleLeft.style.borderBottom = '0'; thisModuleAnchor.style.color = '336699'; } window.status = "Noverant Back-Office"; ]]> </script> </head> 

This transformation assumes that the navigation document will exist in its own frame, as it creates a complete HTML document. The template can easily be modified to share a frame with other UI elements.

In the following section of code the template iterates over the top-level <navitem> elements and creates tab folders for each of them. JavaScript methods are attached to onClick( ), onMouseOver( ), and onMouseOut( ) event handlers.

 <body marginwidth="0" marginheight="0" leftmargin="0" topmargin="0"> <div  style="display:inline"> <table cellspacing="0" cellpadding="0" border="0"> <tr> <xsl:for-each select="children/navitem"> <td height="25" valign="top" > <xsl:choose> <xsl:when test="position() = 1"> <xsl:attribute name="style">background-color:white </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="style">background-color: steelblue;border-bottom:1px solid;border-color:336699</xsl:attribute> </xsl:otherwise> </xsl:choose> <img src="/books/4/456/1/html/2/images/left.gif" width="4" height="4"/> </td> <td align="center" height="25"   nowrap="true"> <xsl:choose> <xsl:when test="position() = 1"> <xsl:attribute name="style">padding:2px; background-color:white</xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="style">padding:2px; background-color:steelblue;border-bottom:1pxsolid; border-color:336699 </xsl:attribute> </xsl:otherwise> </xsl:choose> <a  href="javascript: showFunction('{position()}');" > <xsl:attribute name="onMouseOver">JavaScript:window. status='<xsl:value-of select="description"/>'; return true; </xsl:attribute> <xsl:attribute name="onMouseOut">JavaScript: window.status='Noverant Back-Office'; return true;</xsl:attribute> <xsl:if test="position() = 1"> <xsl:attribute name="style">color:336699 </xsl:attribute> </xsl:if> <xsl:value-of select="description"/></a> </td> <td height="25" valign="top" > <xsl:choose> <xsl:when test="position() = 1"> <xsl:attribute name="style">background-color:white </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="style">background-color: steelblue;border-bottom:1px solid;border-color:336699</xsl:attribute> </xsl:otherwise> </xsl:choose> <img src="/books/4/456/1/html/2/images/right.gif" width="4" height="4"/> </td> <td width="1" bgcolor="C0C0C0"><img src="/books/4/456/1/html/2/images/spacer.gif" 
width="1" height="25"/></td> </xsl:for-each> </tr> </table> </div>

The XSL generate-id( ) function is invaluable for creating unique IDs to attach to elements used in your output document. The generate-id( ) guarantees that it will produce a unique and repeatable ID for every node in the source document.

At this point the template iterates over all of the second-level <navitem> elements and creates a list of hyperlinks out of them. These hyperlinks will appear below the tab folders.

 <xsl:for-each select="children/navitem"> <xsl:variable name="i" select="position()"/> <div > <xsl:choose> <xsl:when test="position() != 1"> <xsl:attribute name="style">position:absolute; display:inline;visibility:hidden;width:100%;background-color:white; border-bottom:1px solid;border-color:steelblue</xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="style">position:absolute; display:inline;width:100%;background-color:white;border-bottom:1px 
solid;border-color:steelblue</xsl:attribute> </xsl:otherwise> </xsl:choose> <table cellspacing="0" cellpadding="0" border="0" style= "padding:2px"> <tr> <td style="color:336699" > <xsl:attribute name="nowrap"/> <xsl:for-each select="children/navitem"> <xsl:variable name="j" select="position()"/> <xsl:variable name="functionVar" select= "concat($i,'_',$j)"/> &#160; <a name="{generate-id()}"
href="controller.asp?view={href}" target="main"> <xsl:attribute name="onMouseOver">JavaScript: window.status='<xsl:value-of select="description"/>'; return true; </xsl:attribute> <xsl:attribute name="onMouseOut">JavaScript: window.status='Noverant Back-Office'; return true;</xsl:attribute> <xsl:value-of select="description"/> </a> &#160; <xsl:if test="position() != last()">&#160;|&#160; </xsl:if> </xsl:for-each> </td> </tr> </table> </div> </xsl:for-each> </body> </html> </xsl:template>

A call to this page from frameset will produce the page in Figure 6-6.

Figure 6-6 A tab-folder approach to navigation.

Creating a Navigation Tree

For the second implementation, the same navigation XML can be used to create a tree-based navigation system. By rewriting the navigation XSLT, a page designer can create a drastically different front-end. This example of an XSLT used to produce a tree demonstrates the flexibility that can be achieved by separating content from presentation.

Like the menu transformation, the tree transformation assumes that the resulting document will reside in its own frame.

 <xsl:template match="navigation[@type='tree']"> <html> <head> <script language="JavaScript"> function expand(divid) {  eval("closed_"+divid).style.display = 'none';  eval("open_"+divid).style.display = ''; } function collapse(divid) {  eval("open_"+divid).style.display = 'none';  eval("closed_"+divid).style.display = ''; } </script> <style> a:visited {background-color:#C7D2DE; color:black;  text-decoration: underline} a:link {background-color:#C7D2DE; color:black; text-decoration: underline} a:active {background-color:activecaption; color:captiontext; text-decoration: underline} a:hover {background-color:#C7D2DE; color:black;  text-decoration: underline} .clsHeading {font-family: verdana; color: black; font-size: 11; font-weight: 800;} .clsEntryText {padding-top: 2px; font-family: verdana; color: black; 
font-size: 11; font-weight: 400; background-color:#C7D2DE;} .clsWarningText {font-family: verdana; white-space:pre; color: #B80A2D;
font-size: 11; font-weight: 600; width:550; background-color:#EFE7EA;} .clsCopy {font-family: verdana; color: black; font-size: 11;
font-weight: 400; background-color:#FFFFFF;} </style> </head> <body style="background-color:#C7D2DE;"> <table width="100%" cellspacing="5"> <tr> <td nowrap="yes"> <div style="padding-left:5px" > <xsl:if test="openIcon"><img src="/books/4/456/1/html/2/images/{openIcon}"/> &#160;</xsl:if> <xsl:if test="href"><a href="{href}"> <xsl:if test="href/@target"><xsl:attribute name= "target"><xsl:value-of select="href/@target"/></xsl:attribute> </xsl:if> <xsl:value-of select="description"/></a> </xsl:if> <xsl:if test="not(href)"><xsl:value-of select= "description"/></xsl:if> <xsl:apply-templates select="children"/> </div> </td> </tr> </table> </body> </html> </xsl:template>

The recursive nature of the navigation tree schema suits XSLT perfectly. <navitem> elements can be transformed in place with little conditional logic.

 <xsl:template match="navitem"> <xsl:choose> <xsl:when test="children"> <xsl:variable name="childcnt" select="count(.//navitem)"/> <div  > <xsl:for-each select="ancestor::navitem"> <xsl:choose> <xsl:when test="count(following-sibling::navitem) > 0"> <img src="/books/4/456/1/html/2//xmlbook/images/line.gif"/> </xsl:when> <xsl:otherwise> <img src="/books/4/456/1/html/2//xmlbook/images/nothing.gif"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <img src="/books/4/456/1/html/2//xmlbook/images/folder_plus.gif" style="cursor:hand" 
onClick="expand('{generate-id()}')"/> <xsl:choose> <xsl:when test="closedIcon"><img src="/books/4/456/1/html/2/images/{closedIcon}" />&#160;</xsl:when> <xsl:when test="icon"><img src="/books/4/456/1/html/2/images/{icon}"/>&#160; </xsl:when> </xsl:choose> <xsl:if test="href"><a href="{href}"> <xsl:if test="href/@target"><xsl:attribute name= "target"><xsl:value-of select="href/@target"/></xsl:attribute> </xsl:if> <xsl:value-of select="description"/></a> </xsl:if> <xsl:if test="not(href)"><xsl:value-of select="description"/> </xsl:if> (<xsl:value-of select="$childcnt"/> artist<xsl:if test= "$childcnt != 1">s</xsl:if>) </div> <div style= "display:none"> <xsl:for-each select="ancestor::navitem"> <xsl:choose> <xsl:when test="count(following-sibling::navitem) > 0"> <img src="/books/4/456/1/html/2//xmlbook/images/line.gif"/> </xsl:when> <xsl:otherwise> <img src="/books/4/456/1/html/2//xmlbook/images/nothing.gif"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <img src="/books/4/456/1/html/2//xmlbook/images/folder_minus.gif" style= "cursor:hand" onClick="collapse('{generate-id()}')"/> <xsl:choose> <xsl:when test="openIcon"><img src="/books/4/456/1/html/2/images/{openIcon}" />&#160;</xsl:when> <xsl:when test="icon"><img src="/books/4/456/1/html/2/images/{icon}"/>&#160; </xsl:when> </xsl:choose> <xsl:if test="href"><a href="{href}"> <xsl:if test="href/@target"><xsl:attribute name= "target"><xsl:value-of select="href/@target"/></xsl:attribute> </xsl:if> <xsl:value-of select="description"/></a> </xsl:if> <xsl:if test="not(href)"><xsl:value-of select="description"/> </xsl:if> (<xsl:value-of select="$childcnt"/> artist<xsl: if test="$childcnt != 1">s</xsl:if>) <xsl:apply-templates select="children"/> </div> </xsl:when> <xsl:otherwise> <div > <xsl:for-each select="ancestor::navitem"> <xsl:choose> <xsl:when test="count(following-sibling::navitem) > 0"> <img src="/books/4/456/1/html/2//xmlbook/images/line.gif"/> </xsl:when> <xsl:otherwise> <img src="/books/4/456/1/html/2//xmlbook/images/nothing.gif"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <img src="/books/4/456/1/html/2//xmlbook/images/divider.gif" onClick= "collapse('{generate-id()}')"/> <xsl:if test="icon"><img src="/books/4/456/1/html/2/images/{icon}"/>&#160;</xsl:if> <xsl:if test="href"><a href="{href}"> <xsl:if test="href/@target"><xsl:attribute name= "target"><xsl:value-of select="href/@target"/></xsl:attribute> </xsl:if> <xsl:value-of select="description"/></a> </xsl:if> <xsl:if test="not(href)"><xsl:value-of select="description"/> </xsl:if> </div> </xsl:otherwise> </xsl:choose> </xsl:template>

Figure 6-7 shows what the Noverant back-office application looks like using this transformation.

Figure 6-7 A tree-based navigation system.

An XML-based navigation system is inherently extensible. Its presentation rules can be changed in a variety of ways to dramatic effect. Another alternative to the two examples shown here is to develop a transformation that creates drop-down menus. This exercise, however, will be left to the reader.



XML Programming
XML Programming Bible
ISBN: 0764538292
EAN: 2147483647
Year: 2002
Pages: 134

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