To read the article online, visit http://www.4GuysFromRolla.com/articles/092204-1.aspx

Creating a Tabbed Interface for Displaying Parent/Child Data

By Scott Mitchell


Introduction


Last week I wrote an article titled Creating Collapsible Detail Regions in a Repeater, which looked at how to use a Repeater and some client-side JavaScript and HTML markup to help manage information overload. (Be sure to check out the live demo of the collapsible regions Repeater!) After writing last week's article, I got to thinking about how this concept could be enhanced to provide a "prettier" user interface. While I am the first to admit that I am anything but graphically and artistically inclined, I think my efforts to date on prettying up the interface are worth sharing. (See for yourself at the live demo.)

My efforts resulted in creating a tabbed interface for displaying parent/child data (although it could be used just like in the collapsible regions Repeater example, showing details for a particular item). The user interface has the parent items listed in a series of vertically arranged tabs on the left, with the details displayed on the right. Clicking a tab "selects" the tab, and displays its details in the pane to the right.

The end goal of this is to create a custom ASP.NET server control that will allow one to create a tabbed interface by simply dropping a Web control from the Toolbox in Visual Studio .NET onto the Designer. That project, though, will likely take some time, so in the interim I thought it would be worthwhile to share the Repeater and client-side script and markup. My reasoning for this is two-fold: first, it will hopefully be useful for some folks out there, who can incorporate this look and feel into their site; second, the client-side script and markup could use some work. It looks great in Internet Explorer, but, while functional, is not nearly as pleasant in FireFox. (I've yet to test it with browsers other than IE or FireFox.) My hope is that someone with more CSS and DHTML experience than I can provide some pointers on improving things.

Laying Out the Tabs


Before continuing, be sure to check out the live demo so that you are familiar with the look and feel of the output.

When crafting the client-side markup to generate the tabbed interface, I wanted to have the markup generated so that parent and child information followed one another in the generated HTML. This is because my assumption was that the Repeater control being used would be grabbing the parent and child data together, similar to how the abbreviated and detailed information was accessed in the collapsible regions Repeater article from last week. That is, I expected to have a Repeater with a declarative syntax like so:

<asp:Repeater runat="server" ...>
  <ItemTemplate>
    ... render parent (or abbreviated) information ...
    
    ... render child (or detailed) information ...
  </ItemTemplate>
</asp:Repeater>

With such a Repeater, the HTML rendered on the page would look something like:

... markup for parent (or abbreviated) information for item 1...
... markup for child (or detailed) information for item 1...

... markup for parent (or abbreviated) information for item 2...
... markup for child (or detailed) information for item 2...

    ...

... markup for parent (or abbreviated) information for item N...
... markup for child (or detailed) information for item N...

The challenge was, given this markup, how to layout the elements of the page in the needed fashion. By setting the display CSS property of the child (or detailed) information markup to none, I could effectively hide the detail information, meaning I'd be given a series of parent (or abbreviated) titles, which is what I was after. The challenge was in having the child (or detailed) information, when displayed, displayed so that its upper-left hand corner was flush with the top-most parent tab's upper-right hand corner. The only approach I could see that would work would be to use absolute positioning.

All of the markup is encased within a containing <div> tag. In order to layout the tabs and child/details data correctly, I created a client-side JavaScript function, setInitialPositions(), that is called after the page has completely loaded. This function sets the absolute positions of the tabs and child/details panes, using the following formulas:

Details Top Coordinates = Calculated Top coordinates of the containing <div>
Details Left Coordinates = Calculated Left coordinates of the containing <div> + the width of the tabs + 1
If the containing <div> does not have an explicitly set height, the setInitialPositions() also determines the height of the maximum detail item, and assigns this value to the height of the containing <div>. (This causes all child/detail items, regardless of how much information they contain, to have the height of the tallest child/details item.) To determine the top and left coordinates of the containing <div> I use two recursive functions, getAscendingTops(element) and getAscendingLefts(element), which walk up the passed-in element's set of containing HTML element, summing up their top and left offsets. A more thorough discussion of this technique can be found at Determining the Location of a Nonpositioned Element, and is the technique utilized by my free, open-source ASP.NET menu control, skmMenu.

Hiding and Showing Child Data When a Parent Tab is Clicked


When a parent tab is clicked the associated child/details data needs to be displayed. This is accomplished by adding a client-side onclick event handler to each tab that calls the JavaScript function showTopic(child/details index). The child/details index is the index of the child/details data. The first parent tab's child/details data has an index of 0, the second 1, and so on. The showTopic() function performs the following tasks:
  1. Hides the currently selected child/details information (by setting the display property to none).
  2. Resets the currently selected tab so that is appears "unselected."
  3. Shows the child/details information corresponding to the tab that was clicked (by setting the display property to inline).
  4. Makes the clicked tab appear "selected," by changing its background color and right border

Displaying Database Data in the Tabbed Interface


In order to display parent/child or abbreviated/detailed database information in the tabbed interface you will need to do the following:
  1. Create an ASP.NET page with the requisite client-side JavaScript functions,
  2. Add a Repeater with the appropriately marked up contents within its templates, and
  3. Add the server-side code that binds the appropriate database data to the Repeater.
Let's examine the markup needed in the Repeater. Essentially the Repeater needs to emit two <div> elements per item - one that contains the parent or abbreviated information, and the other containing the child or detail information for that particular parent/abbreviated data. Earlier I mentioned that the entire tabbed interface markup is encased within a containing <div>. This can be defined around the Repeater's declarative syntax, or injected within the Repeater's HeaderTemplate and FooterTemplates. In a nutshell, the Repeater templates and markup will look like:

<asp:Repeater runat="server" id="rptTabInterface">
  <HeaderTemplate>
    <div id="container" style="width:100%;height:350px">
  </HeaderTemplate>
  
  <ItemTemplate>
    <div id='h<%# DataBinder.Eval(Container, "ItemIndex")%>' 
         onclick='showTopic(<%# DataBinder.Eval(Container, "ItemIndex")%>);' 
         style="cursor:pointer;cursor:hand;position:relative;z-index:2;
                border-left: solid 1px black;width:20%;background-color:#aaaaaa;
                border-right: solid 1px black;border-bottom: solid 1px black;padding:5px;">
      ... Parent/abbreviated data goes here ...
    </div>
  
    <div id='d<%# DataBinder.Eval(Container, "ItemIndex")%>' 
            style="overflow:auto;z-index:1;border: solid 1px black;width:80%;
                   position:absolute;background-color:#eeeeee;padding:5px;display:none;">
      ... Child/detail data goes here ...
    </div>
  </ItemTemplate>
  
  <FooterTemplate>
    </div>
  </FooterTemplate>
</asp:Repeater>

In the example above, the containing <div> is placed within the HeaderTemplate and FooterTemplate. Note that its height is set to 350px. This will cause the tabbed interface to have a height of precisely 350px regardless of how much space the child/details data requires. Child/detail information that exceeds 350px will contain a vertical scrollbar. If you omit this explicit height setting in the containing <div> the height of the tabbed interface will be set to the height needed by the tallest child/details data.

In the ItemTemplate there are two <div> elements. The first one, whose id equals hIndex, where Index is the index of the current RepeaterItem, contains the parent or abbreviated information. Whatever contents you place within this first <div> will be what appears in the corresponding tab. The second <div> in the ItemTemplate contains the child/details data; it has an id of dIndex. (These ids are important to note, as they are used in the client-side functions to programmatically reference and alter the styles of the HTML elements.)

Both <div> elements in the ItemTemplate contain styles that define their background colors, border behavior, padding, overflow behavior, and so on. You can tweak some of these settings, such as background-color, to customize the tab interface for your site. All of the work of explicitly positioning the detail items, and altering the styles based on what tab is clicked, is handled by JavaScript functions, which can be seen (and copied from) the live demo.

One last comment - it is important that, at the bottom of your page, you call the setInitialPositions() function. Also, you can opt to have the first tab automatically displayed by calling showTopic(0). I accomplish this by adding the following <script> block after the Repeater:

<script language="JavaScript">
   setInitialPositions();
   showTopic(0);
</script>

Conclusion


In this article we looked at how to implement a tabbed interface that can show parent/child or abbreviated/detailed information. This was accomplished by using some client-side JavaScript along with a Repeater control to render the data in appropriately formatted markup. The end goal of this project is to create a custom server control that can provide this output with the ease of dragging and dropping a control onto an ASP.NET Web page. For the time being, however, to implement this on your site you'll need to visit the live demo and copy and paste the code in its entirety (although there are a number of places you can use to customize the appearance of the tabbed interface).

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • View a live demo!
  • Article Information
    Article Title: ASP.NET.Creating a Tabbed Interface for Displaying Parent/Child Data
    Article Author: Scott Mitchell
    Published Date: September 22, 2004
    Article URL: http://www.4GuysFromRolla.com/articles/092204-1.aspx


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers