4umi.com/web/javascript/tab

Tab-a-page

Dynamic Javascript

This page explores the topic of tabs in HTML pages.

The idea

Introduction

A tabbed page offers a convenient and elegant solution for communicating greater amounts of segmented information. What might otherwise be a page cluttered with incoherent fields and boxes left and right, where everything seems lost, becomes a convenient collection of mini-pages. To build this interface dynamically from an ordinary, fully accessible page is the object of this script, which combines HTML, Javascript and CCS. Because within the stringent borders of these tabs hides an undreamed of freedom.

Que pasa?

If all is well, you just saw it happen—or it happened before you saw anything. When the document is loading and the document tree (the dom) is built, a Javascript function is called which looks for a certain constellation of elements in its branches. Every <h3> with a certain name in its class attribute is copied to a new element, a tab, and all tabs are inserted into the page at the point where the first <h3> was found. The original headings are then hidden from view. Each is expected to precede some content inside a <div>, which is displayed when the corresponding tab is clicked.

Additional functionality

left arrow key right arrow key

When a tab has focus, it is underlined according to the current style. Pressing the common arrow-left and arrow-right keys will then toggle the previous or next tab. The tabs can be right-clicked, opened in a new window, and bookmarked, just like ordinary links. In fact, they are quite ordinary, they can even be linked to in hardcore handcoded HTML:

would render as:

and would land visitors from anywhere in the world at the same place. From within the document, it is possible to navigate around with code like dotab(2); or similar.

HTML

Valid Code

A first requirement for any script to run properly, or any css layout to succeed, is impeccable html in the source code. One innocent forgotten </b> tag may result in very different documents with various recipients, if their browsers are willing to handle the code at all. A proper doctype declaration is recommended for correct placement and alignment of the elements across browsers, but is not required for the script to build the tabs and toggle the display between them.

Headings and divisions

The script has been designed to degrade as gracefully as possible. All content is normally visible and accessible to users (and bots) with no Javascript. The structure reflects the semantic meaning of the tags. The initializing function looks for a certain constellation of elements in the branches of the DOM: all <h3> elements with a given class are recognized as wannabe-tabs, and if they are followed by a <div> element, they pass the test and are relocated to form a row of elements above an outlined area where the main content is displayed. The content in this slab is taken from said <div>. The row is positioned at the location of the first conforming element, any other content will move down the required amount.

<h3 class="tabme">HTML</h3>

<div>
<h4>Valid Code</h4>
<p>A first requirement ... is the limit.</p>
</div>

<h3 class="tabme">Script</h3>
...

The sky is the limit.

Script

Global variables

The script consists of two functions and one global variable to hold the information needed during the lifetime of the document in the browser. An initializing statement completes the code.

Initialing during load

The initialing function may be called any time after the last tag element has been seen by the browser. In this example page, the script is placed in the head section of the document, except for the calling statement, which is put in a separate script element in the body, behind the last </div> end tag of the div elements which are to be tabs. This ensures that all elements required for a succesful operation have fully loaded, without depending on the window onload event. The script does not need to wait for unrelated objects such as images or other embedded content to load, it can run straight away, rearranging that part of the dom that has already rendered.

Initialing onclick

Alternatively, the rearrangement of the dom may be offered to the user via a link or a button. This way, the tabbed interface becomes optional. Likewise, to syntax highlight the code below takes only a click.

var Tab = { cur:0 };

function initab( classname ) {
 var tabs = [], divs = [], texts = [], f, g, h, i = 0, j = 0, k, o,
  d = document, a = d.getElementsByTagName('h3'),
  b = d.createElement('div'), c = d.createElement('a'),
  n = function(h) { while((h = h.nextSibling)) {
   if( /div/i.test( h.tagName ) || '' ) { return h; }
   return null; }
  },
  y = function() { return false; },
  z=function(e) { e = e || window.event || {}; var t;
   if( e.keyCode===37 &&
    ( ( t = this.previousSibling ) || ( t = this.parentNode.lastChild ) )
    || e.keyCode===39 &&
    ( ( t = this.nextSibling ) || ( t = this.parentNode.firstChild ) ) ) {
     if( /a/i.test( t.tagName ) ) { t.focus(); e.returnValue = false; }
     return false;
   }
  };
 c.className = 'tab0'; c.hideFocus = true;
 while( i < a.length ) { h = a[i];
   if( new RegExp( '\\b' + classname + '\\b' ).test( h.className ) &&
    ( g = n(h) ) ) {
     k = h.firstChild && h.firstChild.nodeValue;
     o = c.cloneNode(true); o.href = '?' + ( j + 1 );
     o.appendChild( d.createTextNode( k || c || 'Tab' ) );
     o.onfocus = new Function( 'return dotab(' + j + ')' );
     o.onclick = y;
     o.onkeydown = z;
     g.className = 'slab0'; h.className = 'slab0';
     b.appendChild(o); tabs[j] = o; divs[j] = g; texts[j] = k;
     if(!f) { f = h; } j++;
   }
   i++;
 }
 f.parentNode.insertBefore( b, f );
 Tab.tabs = tabs; Tab.divs = divs; Tab.texts = texts; i = 0;
 if( window.location.search ) {
  j = parseInt( window.location.search.substring(1) ) - 1;
  if( tabs[j] ) { i = j; }
 }
 dotab(i);
}

function dotab(i) {
  Tab.tabs[Tab.cur].className = 'tab0';
  Tab.divs[Tab.cur].className = 'slab0';
  Tab.cur=i;
  Tab.tabs[i].className = 'tab1';
  Tab.divs[i].className = 'slab1';
  return false;
}

Depending on various circumstances, there may be more efficient ways to handle the switch.

Style

Tags and classes

It is possible to style the normal, non-javascript enhanced appearance of the page and the Javascripted tab blocks in quite different ways. In fact, this is quite likely to be the case, as not only are there different classes, but more importantly, the tags visible on the page are not at all the same as the ones in the code. The links that are the tabs are <a> elements, but they were heading tags in the source code.

Printing

Some special attention to a printer stylesheet is rewarding. Have a look at the Print Preview... under your browser's File menu. The dynamically created links that function as the tabs, can be hidden from the printer. It is probably the best in most situations to make the printed version appear with the original lay-out just like a non-javascript page.

The current skin

If the positions and borders and such are the bones, then the text-decoration and color values are not merely decoration at all but the skin.

@media screen,projection {
a.tab0, a.tab1 {
	position:relative; left:20px;
	border:2px outset white; border-bottom:0; padding:1px 6px;
	background: #e4e0d8; color:black; text-decoration: none;
	font:90% 'MS Sans Serif', sans-serif;
}
a.tab1 {
	background:#f4f0e8; padding:3px 6px; top:-1px;
}
a.tab0:active, a.tab1:active {
	text-decoration:underline;
}
a.tab0:hover, a.tab1:hover {
	text-decoration:overline;
}
.slab0 {
	display:none;
}
.slab1 {
	display:block;
	width:700px; height:400px; overflow:auto;
	border:2px outset white; padding:0.5em;
	background:#f4f0e8; color:black;
}
}
@media print {
a.tab0, a.tab1 { display:none; }
}

The url to the latest default template used for our tabs is 4umi.com/web/css/tab.css. The base for the above, the darker version colour #d4d0c8, is Windows' silvery background of old.

A word of caution

Warning

The example is taking place as we speak, in front of our very eyes, at the tip of the mouse. Errors in the HTML code may not immediately stop the script, but instead result in a malformed document. The flexible setup of the script allows clever combining of elements to produce all sorts of strange tabs alongside ordinary ones. We like to believe in the tabs as physical objects, ... yet another dream shattered.

There may be more and other content following the tabbed divisions. This paragraph or <p> element is a genuine nextSibling of the last enchanted <div>. The heading above it however, which speaks of “caution”, appears before the last tab in the source, and on the printed version, and to users without Javascript, and search engine robots, and quite likely many others.