Jan Miksovsky’s BlogArchive2012 AboutContact

UI Control of the Week: TabSet for traditional tabbed pages

This week's control is the standard tabbed page UI found throughout client apps and web sites. Here's a typical example, from iTunes Preferences:

ITunes Preferences

Key attributes

TabSet control

I've posted a TabSet control in the QuickUI Catalog that manages a set of tabs:

TabSet

The pages within the TabSet can be any type of element or control, although for convenience a Tab control is provided to make it easy to set the page's descriptive label.

Usage: Use a TabSet when you need to fit a large number of controls into a comparatively small space, and the controls can be grouped into meaningful tabs with clear labels. The controls in each tab should generally only have local effects within that tab's UI; it would be extremely confusing if checking a box in one tab disabled some control on a different tab.

A scrolling page may often be a simpler alternative to a tabbed UI. One advantage tabs do have is that the labeled tab buttons provide a summary; they help give the user an overview of what object properties, navigational areas, etc., are available. To the extend the tab labels are meaningful and clearly reflect the tab's contained controls, this labeled structure may accelerate a user's search for a particular control.

Implementation notes

I've built TabSet top of a more primitive control called Switch. Switch acts as a container for other elements, and will only show one of those elements at a time. (The "Switch" name is inspired by the "switch" statement in programming languages like C and JavaScript.) There are actually plenty of cases where a UI will contain a mutually-exclusive set of elements, and not all of these cases happen to look like tabs, so upon reflection it's somewhat surprising to me that more UI toolkits don't offer something like a Switch control.

In this case, the TabSet wraps a Switch, adding a List of buttons and populating them with the description() property of the corresponding tabs.

The trickiest part of TabSet turned out to be handling the common case in which the TabSet itself should be as tall as its tallest tab (regardless of the individual tab heights). This allows for a consistent border or background, which helps the user interpret the disparate tabs as being closely related; it also avoids potential document reflow when the user switches tabs. The standard ad hoc solution in a case like this is to force all the elements to a known height (e.g., in pixels), but hard-coding element sizes seems like a cop-out if one's goal is to create a flexible control that handle a wide range of content. It seems like TabSet (or, actually, Switch) should be able to inspect the height of its contained elements and automatically resize itself to be as tall as the tallest contained element. This gets tricky because Switch normally hides all pages except the one which is active, and the height of an element hidden with display: none is reported as zero. To work around this, the underlying Switch class has been modified so that, in the auto-maximizing case like this, Switch hides the inactive pages with visibility: hidden instead (which lets the elements report their correct height), then uses absolute positioning to superimpose and top-align the pages.

A related complexity arose in the case shown in the TabSet demo: the height of a tab may change based on asynchronously loaded content (e.g., an image). So the update of any tab's content, even one which isn't currently visible, may potentially force the TabSet to resize. Unfortunately, there isn't a standard DOM resize event for elements other than elements the user can resize (such as the window). So QuickUI controls have to make do by raising a custom event when they resize, allowing controls like Switch to adjust their height accordingly.

It's boring details like resizing that forces most designers to throw up their hands and resort to hard-coded pixel dimensions, but UI controls that can flexibly handle dynamic content are ultimately far easier to use and work with as a design evolves.