asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Advanced UI Design Using XML and XSL - Part 2: Custom Context Menu Creation
By Joe Slovinski
Rating: 4.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction


    Welcome to Advanced UI Design Using XML and XSL. Each article in my series will demonstrate the creation of a specific user interface (UI) component using eXtensible Markup Language (XML) and eXtensible Stylesheet Language (XSL).

    This article explains how to create a custom, object-specific context menu of unlimited depth by using XML and XSLT. The context menu discussed here functions by taking an XML feed of a specific format and applying an XSLT style sheet to it. The resulting transformation is then printed to the client. The client handles all user requests to navigate the menu. The client used in this article is Internet Explorer 5.5+.

    This context menu can be applied to any object within your specific Web page(s). For demonstration purposes within this article I am applying the context menu to objects within a folder tree.

    The folder tree used is the same folder tree discussed in a previous 15 Seconds article (see http://www.15seconds.com/issue/010921.htm). As noted within the referenced article, I have added a special element to each entity within the tree's XML. This element is called "onContextMenu" and references a specific XML file that contains the makeup of the requested context menu.

    The actual context menu demonstrated is available in the Download section below.

    Context Menu Overview

    Within Windows applications it is common to right click on objects and receive object-specific context menus. For instance, right clicking on rows within an SQL Server table, rows within an Excel spreadsheet, or simply right clicking on your computer's desktop produces custom context menus.

    The options within these context menus are specific to the application being used, and the object within that application that was clicked on. Early Web browsers did not give developers the capability to create context menus for our specific objects. Recent advances within the Document Object Model (DOM) and our Web browsers now make this possible within Web applications.


    Figure 1. An example of an object-specific context menu created within MS Internet Explorer

    This screenshot uses a number system to illustrate the different levels of the menu.

    XML Structure

    The XML format I have chosen is good for a recursive XSLT style sheet that satisfies the requirement within this article for a context menu of unlimited depth.

    The XML document I have constructed contains a root element named "menu" that can contain only "entity" elements. The structure of your context menu is implicitly defined here by nesting option elements within the "contents" element of other entity elements. Below is a listing of all elements or attributes that belong to the "option" element.
    NameTypeDescription
    idAttribute Unique string or integer used to identify the individual option
    descriptionElement Description of individual option. This is the text displayed to the user.
    onClickElement Name of the client-side function fired upon the onClick event
    imageElement Image displayed when option is closed or not selected
    imageOpenElement Image displayed when option is selected
    contentsElement Contains entity elements. The content element is used to determine whether or not an option has a submenu.

    The following XML describes the context menu that is displayed when the user right clicks on a customer. For illustration purposes, all XML is contained in static XML documents instead of being retrieved by XML database queries.

    
    <?xml version="1.0"?>
    <menu>
      <entity id="c1">
        <description>Add Customer</description>
        <image>images/add_small.gif</image>
        <imageOpen>images/add_small.gif</imageOpen>
        <contents>
          <entity id="c2">
            <description>Business</description>
            <image>images/spacer.gif</image>
            <imageOpen>images/spacer.gif</imageOpen>
            <contents>
            </contents>
          </entity>
          <entity id="c3">
            <description>Individual</description>
            <image>images/spacer.gif</image>
            <imageOpen>images/spacer.gif</imageOpen>
            <contents>
            </contents>
          </entity>
        </contents>
      </entity>
      <entity id="c4">
        <description>Modify Customer</description>
        <image>images/modify_small.gif</image>
        <imageOpen>images/modify_small.gif</imageOpen>
        <contents>
        </contents>
      </entity>
      <entity id="c5">
        <description>Remove Customer</description>
        <image>images/x_small.gif</image>
        <imageOpen>images/x_small.gif</imageOpen>
        <contents>
        </contents>
      </entity>
    </menu>
    
    
    The above XML file is named "contextCustomer.xml" and is available for download at the end of this article.

    XSL Style Sheet

    Below is the standard XSLT style sheet that is applied to our XML documents.

    
    <xsl:stylesheet version="1.1"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:dt="urn:schemas-microsoft-com:datatypes">
    <xsl:template match="menu">
    <div style="position: absolute;">
      <div onselectstart="return false" ondragstart="return false">
      <xsl:attribute name="STYLE">
        position: absolute;
        background-color: #6699cc;
        border:1px solid #99ccff;
      </xsl:attribute>
      <table border="0" cellspacing="0" cellpadding="1">
        <tr>
          <td>
            <table border="0" cellspacing="0" cellpadding="0">
              <xsl:apply-templates select="entity"/>
            </table>
          </td>
        </tr>
      </table>
      </div>
      <xsl:apply-templates select="entity/contents"/>
    </div>
    </xsl:template>
    
    <xsl:template match="entity">
    <TR>
    <xsl:attribute name="selected">false</xsl:attribute>
    <xsl:attribute name="background">#6699cc</xsl:attribute>
    <xsl:attribute name="light">#99ccff</xsl:attribute>
    <xsl:attribute name="titlebar">#5389bc</xsl:attribute>
    <xsl:attribute name="image">images/<xsl:value-of select="image"/></xsl:attribute>
    <xsl:attribute name="imageOpen">images/<xsl:value-of select="imageOpen"/></xsl:attribute>
    <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
    <xsl:attribute name="ONCLICK"><xsl:value-of select="onClick"/>;clean()</xsl:attribute>
    <xsl:attribute name="ONMOUSEOVER">
      contextHighlightRow(this);
      <xsl:if test="contents/node()[count(child::*)>0]">
        loadContextMenuSub(this)
      </xsl:if>
    </xsl:attribute>
    <xsl:attribute name="ONMOUSEOUT">contextHighlightRow(this)</xsl:attribute>
      <TD VALIGN="MIDDLE" ALIGN="CENTER" NOWRAP="true">
      <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
      <xsl:attribute name="STYLE">
        background-color: #5389bc;
        border-top:1px solid #5389bc;
        border-bottom:1px solid #5389bc;
        border-left:1px solid #5389bc;
        padding-left: 4px;
        padding-right: 4px;
        padding-top: 4px;
        padding-bottom: 3px;
        cursor: default;
      </xsl:attribute>
      <IMG BORDER="0" HEIGHT="15" WIDTH="15">
        <xsl:attribute name="SRC"><xsl:value-of select="image"/></xsl:attribute>
      </IMG></TD>
      <TD NOWRAP="true">
      <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
      <xsl:attribute name="STYLE">
        font-family: Arial;
        font-size: 11px;
        font-weight: normal;
        color: white;
        background-color: #6699cc;
        border-top: 1px solid #6699cc;
        border-bottom: 1px solid #6699cc;
        padding-top: 2px;
        padding-bottom:2px;
        padding-left: 6px;
        padding-right: 8px;
        cursor: default;
      </xsl:attribute>
      <xsl:value-of select="description"/></TD>
      <TD VALIGN="middle" ALIGN="right" STYLE="padding-right: 6px;" NOWRAP="true">
      <xsl:attribute name="ONCLICK"><xsl:value-of select="@onmousedown"/></xsl:attribute>
      <xsl:attribute name="STYLE">
        background-color: #6699cc;
        border-top: 1px solid #6699cc;
        border-bottom: 1px solid #6699cc;
        border-right: 1px solid #6699cc;
        padding-right: 5px;
      </xsl:attribute>
      <IMG BORDER="0" WIDTH="4">
      <xsl:attribute name="SRC">
        <xsl:choose>
          <xsl:when test="contents/node()[count(child::*)>0]">
            images/opensub.gif
          </xsl:when>
          <xsl:otherwise>
            images/spacer.gif
          </xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
      </IMG></TD>
    </TR>
    </xsl:template>
    
    <xsl:template match="contents">
      <xsl:if test="count(child::*)>0">
      <div onselectstart="return false" ondragstart="return false">
      <xsl:attribute name="STYLE">
        position: absolute;
        background-color: #6699cc;
        border:1px solid #99ccff;
        display: none;
      </xsl:attribute>
      <xsl:attribute name="ID"><xsl:value-of select="../@id"/>Sub</xsl:attribute>
      <table border="0" cellspacing="0" cellpadding="1">
        <tr>
          <td>
            <table border="0" cellspacing="0" cellpadding="0">
              <xsl:apply-templates select="entity"/>
            </table>
          </td>
        </tr>
      </table>
      </div>
      <xsl:apply-templates select="entity/contents"/>
      </xsl:if>
    </xsl:template>
    
    </xsl:stylesheet>
    
    
    Figure 2 displays a context menu that is created using the files included in this article.


    Figure 2. XSL transformation

    Client Operations

    In order for the context menu to function, the following client-side operations are required:

    1. Load context menu
    2. Load context submenu
    3. Highlight row
    4. Clean (Remove any open context menus)
    Below is the code that performs these four operations. This code can be found within the "context.js" file (see the Download section).
    
    var appState = new applicationState()
    
    function applicationState() {
      this.contextMenu = null
    }
    
    function loadContextMenu(path) {
      var xmlDoc
      var xslDoc
      var contextMenu
    
      if(path != "") {
        xmlDoc = new ActiveXObject('Microsoft.XMLDOM')
        xmlDoc.async = false;
    
        xslDoc = new ActiveXObject('Microsoft.XMLDOM')
        xslDoc.async = false;
    
        xmlDoc.load(path)
        xslDoc.load("context/context.xsl")
    
        if(appState.contextMenu != null) appState.contextMenu.removeNode(true)
      
        document.body.insertAdjacentHTML("beforeEnd", xmlDoc.documentElement.transformNode(xslDoc))
        contextMenu = document.body.childNodes(document.body.childNodes.length-1)
    
        contextMenu.style.left = window.event.x
        contextMenu.style.top = window.event.y
    
        appState.contextMenu = contextMenu
        window.event.cancelBubble = true
      }
    }
    
    function loadContextMenuSub(obj) {
      var contextMenu
      var parentMenu
    
      parentMenu = returnContainer(obj)
      contextMenu = document.all[obj.id + "Sub"]
      contextMenu.style.display = "block"
      contextMenu.style.top = obj.offsetTop + parentMenu.style.pixelTop
      contextMenu.style.left = obj.offsetWidth + parentMenu.style.pixelLeft
      parentMenu.subMenu = contextMenu
    }
    
    function contextHighlightRow(obj) {
      var parentMenu
      var subMenu
      var i
    
      parentMenu = returnContainer(obj)
    
      if(obj.selected == "false") {
        for(i=0; i < obj.childNodes.length; i++) {
          obj.childNodes(i).style.borderTop = "1px solid white"
          obj.childNodes(i).style.borderBottom = "1px solid white"
    
          if(obj.childNodes(i).cellIndex == 0) {
            obj.childNodes(i).style.borderLeft = "1px solid white"
          }
          else if (obj.childNodes(i).cellIndex == obj.cells.length-1) {
            obj.childNodes(i).style.borderRight = "1px solid white"
          }
        }
    
        if(parentMenu.subMenu != null && parentMenu != parentMenu.subMenu) {
          subMenu = parentMenu.subMenu
    
          while(subMenu != null) {
            subMenu.style.display = "none"
            subMenu = subMenu.subMenu
          }
        }
        obj.selected = "true"
      }
      else {
        for(i=0; i < obj.childNodes.length; i++) {
          if(i == 0) {
            obj.childNodes(i).style.borderTop = "1px solid " + obj.titlebar
            obj.childNodes(i).style.borderBottom = "1px solid " + obj.titlebar
          }
          else {
            obj.childNodes(i).style.borderTop = "1px solid " + obj.background
            obj.childNodes(i).style.borderBottom = "1px solid " + obj.background
          }
          
          if(obj.childNodes(i).cellIndex == 0) {
            obj.childNodes(i).style.borderLeft = "1px solid " + obj.titlebar
          }
          else if (obj.childNodes(i).cellIndex == obj.cells.length-1) {
            obj.childNodes(i).style.borderRight = "1px solid " + obj.background
          }
        }
        obj.selected = "false"
      }
    }
    
    function clean() {
      var contextMenu
      
      // remove and destroy context menu
      if(appState.contextMenu != null) {
        contextMenu = appState.contextMenu.removeNode(true)
        contextMenu = null
      }
    }
    
    function returnContainer(container) {
      while(container.tagName != "DIV") {
        container = container.parentNode  
      }
      return container
    }
    
    

    Conclusion

    I hope the contents of this article will help increase the quality of your Web applications' interface. If you have any questions, comments, or suggestions, please feel free to e-mail me at the address listed in the About the Author section.

    On a side note, I would like to thank Lee McGraw for helping me proofread and validate this document.

    The third article in this series expands upon the first article to illustrate how to insert, modify, delete, and rename items within the folder tree.

    Download

    View a live demo of the Context Menu (Works only in IE 5.5+ browsers)

    Download the example.zip file that contains the makeup of the context menu, including all XML, XSL, JS, CSS, HTML, and graphics files

    Other Articles in This Series

    Advanced UI Design

    Part 1 -- Folder Tree Creation
    http://www.15seconds.com/Issue/010921.htm

    Part 3 -- Folder Tree Administration
    http://www.15seconds.com/Issue/011113.htm

    Part 4 - Folder Tree Drag and Drop
    http://www.15seconds.com/issue/011129.htm

    Part 5 - Progress Indicator Creation
    http://www.15seconds.com/issue/011212.htm

    Part 6 -- Progress Indicator Usage
    http://www.15seconds.com/issue/020109.htm

    Part 7 -- Relational Folder Tree Lines
    http://www.15seconds.com/issue/020424.htm

    About the Author

    Joe Slovinski has been programming Web applications since 1993. For more information on the code in this article, contact the Joe Slovinski.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    Stonebroom.ASP2XML
    Stonebroom.ASP2XML(c) is an interface component designed to make building applications that transport data in XML format much easier. It can be used to automatically pass updates back to the original data source.
    [Top]
    Other Articles
    Sep 22, 2005 - Implementing Remote Calling Without Using AJAX
    Right now the latest buzzword around town is AJAX. AJAX is an acronym for Asynchronous JavaScript and XML and is a method used to implement remote calling. The problem is that AJAX is only implemented in ASP.NET 2.0. This article will show you one way to implement remote calling without using AJAX or the XMLHttpRequest object. The technique outlined can even be used from classic ASP and is sufficient for most remote calling needs.
    [Read This Article]  [Top]
    Aug 18, 2005 - SQL Server 2005 XQuery and XML-DML - Part 3
    This article is the third and final installment of Alex Homer's series covering the new XML support in Microsoft SQL Server 2005. In it he covers updating the contents of xml columns, comparing traditional XML update techniques with XQuery, and using XQuery in a managed code stored procedure.
    [Read This Article]  [Top]
    Aug 11, 2005 - SQL Server 2005 XQuery and XML-DML - Part 2
    In the second part of his series on SQL Server 2005's new XML support, Alex Homer looks at extracting data from XML columns, comparing traditional XML data access approaches with XQuery, and combining XQuery and XSL-T.
    [Read This Article]  [Top]
    Aug 3, 2005 - SQL Server 2005 XQuery and XML-DML - Part 1
    Microsoft SQL Server 2005 now offers great support for and close integration with XML as a data persistence format. In the first article of his series examining this new support, Alex Homer offers an overview of how SQL Server 2005 stores XML documents and schemas, examines how it supports querying and manipulating XML documents, and provides a simple test application that allows you to experiment with XQuery.
    [Read This Article]  [Top]
    Jun 30, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 3, Cont'd
    In the final article of his series on reading and writing XML in .NET 2.0, Alex Homer looks at how the updated XML document store objects XmlDocument, XmlDataDocument and PathDocument can be used to read, persist and write XML documents and fragments more easily and more efficiently than in .NET 1.x.
    [Read This Article]  [Top]
    Jun 29, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 3
    In the final article of his series on reading and writing XML in .NET 2.0, Alex Homer looks at how the updated XML document store objects XmlDocument, XmlDataDocument and PathDocument can be used to read, persist and write XML documents and fragments more easily and more efficiently than in .NET 1.x.
    [Read This Article]  [Top]
    Jun 16, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 2, Cont'd
    Alex Homer continues his series on reading and writing XML in .NET 2.0. In part one, we focused on the reading side of things, examining the XmlReader and XmlReaderSettings classes. In this article, we move on to look at the XmlWriter and XmlWriterSettings classes, and how they can be used to write XML documents and fragments more easily and more efficiently than in version 1.x of .NET.
    [Read This Article]  [Top]
    Jun 15, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 2
    Alex Homer continues his series on reading and writing XML in .NET 2.0. In part one, we focused on the reading side of things, examining the XmlReader and XmlReaderSettings classes. In this article, we move on to look at the XmlWriter and XmlWriterSettings classes, and how they can be used to write XML documents and fragments more easily and more efficiently than in version 1.x of .NET.
    [Read This Article]  [Top]
    Jun 2, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 1, Cont'd
    In the first part of his series on reading and writing XML in .NET 2.0, Alex Homer discusses the XmlReader and XmlReaderSettings classes. The XmlReader exposes several useful new features and the all new XmlReaderSettings class makes it easy to generate single or multiple instances of an XmlReader with a range of useful properties.
    [Read This Article]  [Top]
    Jun 1, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 1
    In the first part of his series on reading and writing XML in .NET 2.0, Alex Homer discusses the XmlReader and XmlReaderSettings classes. The XmlReader exposes several useful new features and the all new XmlReaderSettings class makes it easy to generate single or multiple instances of an XmlReader with a range of useful properties.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry

    internet.commediabistro.comJusttechjobs.comGraphics.com

    Search:

    WebMediaBrands Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Shopping | E-mail Offers | Freelance Jobs