Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Salvage Your Client-Side JavaScript Menus in ASP.NET Using XML/XSLT : Page 2

ASP.NET's server-based event-handling model has made working with many popular client-side JavaScript menu systems increasingly unpalatable in their current form. Fortunately, XML and XSLT provide a way out.

XML and XSLT to the Rescue
The basic idea here is to use XSLT to create an existing HTML/JavaScript template by transforming XML documents that adhere to a simple schema accommodating a menu hierarchy. To modify or extend the menu, you need merely modify the XML file rather than the complex combination of HTML and JavaScript code, which is the output of the transformation.

Figure 1. Desired Menu Structure: Each menuItem element may have a collection of menuItem children. The leaf elements (the last children in a branch) hold URLs for redirecting the browser.
Figure 1 shows a desired menu structure where the <menuItem> element has children of its own type—similar to the TreeNode element of the TreeView control. The leaf <menuItem> elements (the last children in a branch) each point to a URL where the browser is directed when users click a leaf. Because <menuItem> elements can have children of their own type, it is not possible to show a schema diagram for this tree.

Clearly, this is an easier structure to modify and extend than the convoluted combination of HTML tags and/or JavaScript arrays, particularly for complex menus such as Hiermenus. XSLT does the hard work of transforming the XML tree to the desired HTML/JavaScript code. This fits well with ASP.NET's server-based methodology. You can execute the transformation logic in the page-rendering code (the Page_Load event handler) to render the results on the page during page construction. Moreover, the technique is generic, and you can apply it to any HTML/JavaScript template.

A Short Example
Here's an example that elucidates the methodology. First, you need an XML representation of the menu tree shown in Figure 1. The following XML represents an example menu.

<?xml version="1.0" encoding="utf-8" ?> <rootMenu> <topMenu text="Button1"> <menuItem href="B1I1.htm" text="B1I1" /> <menuItem href="B1I2.htm" text="B1I2" /> <menuItem text="B1I3"> <menuItem href="B1I3I1.htm" text="B1I3I1" /> </menuItem> </topMenu> <topMenu text="Button2"> <menuItem href="B2I1.htm" text="B2I1" /> <menuItem href="B2I2.htm" text="B2I2" /> <menuItem text="B2I3"> <menuItem href="B2I3I1.htm" text="B2I3I1" /> </menuItem> </topMenu> </rootMenu>

I made a slight modification in the element name from the generic XML tree shown in Figure 1 by changing the top level element to <topMenu>. This was required by the particular HTML/JavaScript menu template used as you'll see shortly. To add new nodes you simply add properly nested <menuItem> elements. All <menuItem> elements require a text attribute that displays the text printed on the item when the menu is rendered. The <menuItem> elements without children identify the actual links and therefore require an href attribute pointing to the URL of the page you want the browser to navigate to when a user clicks the menu item. Notice that while the starting XML tree and the resulting HTML/JavaScript template could be anything, the methodology remains the same. The XSLT transformation handles the details and outputs the correct HTML and JavaScript combination for your particular menu.

For this example, I've used Cezary Tomczak's XulMenu. His menu accommodates arbitrary depth and is nicely tied to CSS styles. Despite this choice, you can follow the same logic to produce output that mimics any JavaScript menu system; in other words, it doesn't matter which menu system you're currently using, you can write a suitable XSL transformation for any of them. The HTML/JavaScript for a XulMenu looks like this:

<a class="button" href="JavaScript:void(0)">Button1</a> <div class="section"> <a class="item" href="B1I1.htm">B1I1</a> <a class="item" href="B1I2.htm">B1I2</a> <a class="item" href="JavaScript:void(0)">B1I3 <img class="arrow" src="images/arrow1.gif" width="4" height="7" alt="" /> </a> <div class="section"> <a class="item" href="B1I3I1.htm">B1I3I1</a> </div> </div>

Figure 2: Example XulMenu: Here's how the example menu looks when rendered in a browser.
The top level in this menu has a class="button" attribute that ties the menu to the "button" style defined using CSS. A menu item with children has one or more <div> tags with the class="section" attribute. The menu item with children displays an arrow image. Clicking on these items toggles them between their expanded and collapsed forms. Menu items with no children are simple <a> tags with class="item" for their style. These are the leaf items that redirect the browser to a specific URL when a user clicks them. You can add additional menu items and nested menu items by including additional <a> and <div> tags in the same manner. Figure 2 shows a clip of this menu as rendered in a browser.

Even though this is a simple menu, modifying it isn't trivial; you must add <a> and <div> tags with the proper classes and correct nesting to generate the desired menu—in other words, the form is simply hard to modify. It would be far easier to modify the simpler XML tree form presented earlier and let XSLT rules take care of the transformation. The following XSLT file does exactly that:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="rootMenu"> <html> <body> <table cellspacing="0" cellpadding="0" id="menu1" class="XulMenu"> <tr> <td class="bar"> <xsl:apply-templates /> </td> </tr> </table> </body> </html> </xsl:template> <xsl:template match="topMenu"> <a class="button" href="JavaScript:void(0)"> <xsl:value-of select="@text" /></a> <div class="section"> <xsl:apply-templates select="menuItem" /> </div> </xsl:template> <xsl:template match="menuItem"> <xsl:choose> <xsl:when test="child::*"> <a class="item" href="JavaScript:void(0)"> <xsl:value-of select="@text" /> <img class="arrow" src="/cts/jsxmlmenu/images/arrow1.gif" width="4" height="7" alt="" /></a> <div class="section"> <xsl:apply-templates select="menuItem" /> </div> </xsl:when> <xsl:otherwise> <a class="item"> <xsl:attribute name="href"> <xsl:value-of select="@href" /> </xsl:attribute> <xsl:value-of select="@text" /> </a> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>

The first <xsl:template> item matches the <rootMenu> element in the XML file and outputs the root <html> and <body> tags and the main <table> tag required by the HTML/JavaScript template. This <table> must have an id="menu1" and class="XulMenu" attributes required by the template. It also creates a single <td> element for the table with id="menu1" and class="bar" attributes—again as required by the HTML/JavaScript template. The <xsl:apply-templates /> line ensures that all children of the <rootMenu> XML element are further transformed according to their own XSLT rules.

The next XSL template matches the <topMenu> XML element and outputs a <div> element with a class="section" attribute as required by the HTML/JavaScript template. It also ensures that any nested <menuItem> elements are further processed by the XSL template by calling <xsl:apply-templates /> (like calling a recursive function). Because the <topMenu> element will always have children, the template outputs the <div class="section"> element automatically.

Transforming the <menuItem> XML element is a bit more complex because this element may or may not have children of its own type. If it does, the template renders the <img> element to display the arrow image as well as a <div class="section"> element to hold the child elements. Otherwise it creates a leaf <a> element with its href attribute set to the href attribute of the XML element.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date