Creating a Tabbed Interface for Displaying Parent/Child DataBy Scott Mitchell
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:
With such a Repeater, the HTML rendered on the page would look something like:
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
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 containingIf the containing
Details Left Coordinates = Calculated Left coordinates of the containing
<div>+ the width of the tabs + 1
<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,
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
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:
- Hides the currently selected child/details information (by setting the
- Resets the currently selected tab so that is appears "unselected."
- Shows the child/details information corresponding to the tab that was clicked (by setting the
- 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:
- Add a Repeater with the appropriately marked up contents within its templates, and
- Add the server-side code that binds the appropriate database data to the Repeater.
<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:
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
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
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
in the ItemTemplate contains the child/details data; it has an
are important to note, as they are used in the client-side functions to programmatically reference and alter the styles of
the HTML elements.)
<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
One last comment - it is important that, at the bottom of your page, you call the
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: