Introduction
I recently decided to convert a couple of Java based applications into ASP.NET web apps, and the nature of these apps' output suggested that a tabbed interface would be effective. A web search for examples of tabbed browser interfaces provided a number of HTML/CSS/JavaScript based solutions: some very clever and a few demonstrating elegant use of CSS. Because my conversions require the integration of C# aspx code with the HTML, I was looking for as simple and transparent a solution as possible, supporting just the features my applications require. In the end, I decided it would be worth the effort to roll my own approach to obtain code that would be easy to integrate with the required server-side C# code, and this article presents the framework of my solution. It provides a tabbed interface using about 75 lines of HTML, JavaScript and CSS that supports resizeable bordered output in each tab and provides for printing each tab's content even when the output spans multiple pages. JavaScript is required for this tabbed interface, and it is not designed to degrade gracefully with browsers that do not support JavaScript.
Here's what the demo output looks like:
Background
Typical tabbed interfaces implement an HTML defined selector such as an unordered list of links or group of radio buttons, then style these choices with CSS to look like tabs. The content associated with each tab is enclosed in a div
that is styled as displayed or hidden as each tab is selected or deselected. The implementing JavaScript code reacts to clicking a tab by changing the style of each tab and div
.
Using the Code
We'll look at the HTML body first, then the JavaScript and CSS components.
Each tab is defined by a link in a list item in the "tabSelect
" unordered list and a div
that must have a "class" value of "tabs" and a unique id. The actual tab content replaces the "Tab n content goes here" text. To add a tab, a li
is added to the unordered list incorporating the appropriate showTab(n)
function call and tab
label, and a div
is added in the corresponding (nth) position in the collection of div
s with the "tabs
" class value.
An optional statement defining a button following the "Tab x content goes here" div
in each "tabs
" class supports reformatting of each tab's content for printing by the browser when the "Printer Friendly Format" button is clicked. The content of all of the tabs is in a single HTML file, so printing a single tab's content without reformatting is not intrinsically possible using the browser's print capability. Clicking the back button after viewing the Printer Friendly Format restores the tabbed interface. The nested div
s allow for extraction of the tab's content without the HTML defining the "Printer Friendly Format" button.
<ul id="tabSelect">
<li><a href="#" onclick="showTab(0);"
class="tablinks">This is tab0 label</a></li>
<li><a href="#" onclick="showTab(1);"
class="tablinks">This is tab1 label</a></li>
</ul>
<div class="tabs" id="tab0Content">
<div id="tab0HTML">
Tab 0 content goes here
</div>
<p><input type="button"
value="Printer Friendly Format"
onclick="printFormat(tab0HTML);"></p>
</div>
<div class="tabs" id="tab1Content">
<div id="tab1HTML">
Tab 1 content goes here
</div>
<p><input type="button"
value="Printer Friendly Format"
onclick="printFormat(tab1HTML);"></p>
</div>
The body
tag defines the actions to take when the page is loaded (select leftmost tab) and when it is resized (redefine borders and reselect the tab that was active when the resize action was taken):
<body onload="showTab(0);" onresize="resizeFlag=true;showTab(lastTabSelected);">
The JavaScript showTab(n)
function has a single parameter (the tab to activate) and is used to invoke the style changes that control the appearance of the tabbed output. When first called, the function sets up arrays identifying the links and div
s and determines the number of tabs that have been defined.
Next, the function iterates through the links and div
s, styling each using the setAttribute
function and style property as active or inactive. The background-color
in the selected tab's link style should match the background-color
specified in the style
section for the div.tabs
selector. Note that the bottom padding for the selected tab is slightly larger than for the unselected tabs (.41em vs. .3em). This causes the tab color for the selected tab to overwrite the tab's border, connecting the tab to the tab content. The minimum value that works is browser dependent, but .41 seems to be a good compromise.
Finally, if the resizeFlag
is true
, the dimensions of the div
s are adjusted to account for the changed dimensions of the browser window. This allows the border to be resized.
The printFormat(id)
function uses the innerHTML
property to extract the HTML code associated with a single tab identified by its id, then displays it in a new window that can be printed using the browser's print capability. This web output excludes the tabs, border and "Printer Friendly Format" button. The browser's back button can be used to return to the tabbed output.
var links = new Array();
var divs = new Array();
var tabCount;
var lastTabSelected = -1;
var resizeFlag = true;
function showTab(selectedTab) {
if (lastTabSelected == -1) {
links = document.getElementsByClassName
("tablinks");
divs = document.getElementsByClassName("tabs");
tabCount = Math.min(links.length, divs.length);
}
for (var i = 0; i < tabCount; i++) {
if (i == selectedTab) {
links[i].setAttribute("style",
"background-color: yellow;
font-weight: bold; padding: 0.3em 0.3em 0.41em 0.3em;");
divs[i].style.display = "";
} else {
links[i].setAttribute("style",
"background-color: lightgray;
font-weight: normal; padding: 0.3em 0.3em 0.3em 0.3em;");
divs[i].style.display = "none";
}
if (resizeFlag) {
divs[i].style.height = (window.innerHeight - 60) + "px";
divs[i].style.width = (window.innerWidth - 30) + "px";
}
}
lastTabSelected = selectedTab;
resizeFlag = false;
}
function printFormat(tabId) {
var htmlStr = "<!DOCTYPE html><html><body>" +
tabId.innerHTML + "</body></html>";
document.open(); document.write(htmlStr); document.close();
}
The style
section defines static aspects of the appearance of the tabs and content areas. Note that the hover
pseudo-class employs the !important
option to raise the priority of the tab color when it is under the cursor. If not so flagged, the color assigned in the showTab(n)
function will prevail.
The background color specified in the div.tabs
style should match the color specified in the showTab(n)
function for the active tab.
Setting overflow: auto
in the div.tabs
style definition automatically provides scroll bars if the tab's content overflows the bordered display area.
ul#tabSelect li { display: inline; }
ul#tabSelect { list-style-type: none; margin: 15px 0 0 0; padding: 0 0 0.3em 0; }
ul#tabSelect li a { color: black; border: 1px solid black; border-bottom: none;
text-decoration: none; border-top-left-radius: .5em; border-top-right-radius: .5em; }
ul#tabSelect li a:hover { background-color: lightblue!important; }
div.tabs { border: 1px solid black; padding: 5px; overflow: auto; background-color: yellow; }
Credit Where Credit is Due
While there are quite a few approaches to building a tabbed web page interface accessible through web searches, and there is a lot to be learned by examining the examples found by searching for a subject like "how to create tabs in html page", I'll end by mentioning (and providing links to) just a couple that I found especially interesting and/or useful.
The approach represented by my example was most influenced by this article on elated.com that is somewhat more complicated than my code, but does claim to degrade gracefully when JavaScript is not available.
Another approach that I that I'd count as one of the clever/elegant class of solutions is provided here on css-tricks.com. It makes use of radio buttons as the tab selectors and is implemented purely with CSS: no JavaScript. This article also provides links to several other approaches to providing tabbed content.
Points of Interest
Here is an example of the tab code integrated with an ASP.NET ID3 rule induction application. An interim version of the app may be accessed here: Run e2gRuleInduction id3 Application. In this application, the output displayed in each tab is generated at run time by invoking a method in a server side C# component.