Access options:

Accessible Tab Navigation

author: mike foskett incept: 21st May 2010

last update: 21st June 2010

A progressively enhanced lightweight and accessible tab navigation which doesn't rely on jQuery.

Written to replace a jQuery method which wasn't accessible via the keyboard and didn't work without JavaScript. Currently live on the Tesco direct web site.

Example

Tab One

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique volutpat est, fermentum posuere ligula pretium egestas. Cras id justo nibh, at laoreet nisi. Quisque accumsan faucibus malesuada.

Tab Two

Pellentesque ut sem sapien. Integer vitae dui elit, eu interdum erat. Duis non velit sed leo convallis imperdiet. Etiam ullamcorper imperdiet nisi eu interdum. Phasellus accumsan laoreet accumsan.

Tab Three

Ut suscipit ultrices commodo. Aliquam erat volutpat. Aliquam erat volutpat. Aenean sed odio augue. Cras pellentesque accumsan ipsum, at molestie dolor malesuada laoreet. Suspendisse nec pharetra orci.

A complete commented bare bones demo version is available. As is a minified JavaScript [1.16 KB] version.

Caveat: This code will only produce a single tab navigation an a page. If you require more than one it means duplicating the JavaScript and changing the id's. Not the best solution but works. Live example with two separate tab navigations: Tesco mini-site for AMD. I may further this project to correctly accommodate multiples in the future.

The XHTML

Basically a list of anchor links which reference div blocks.


<ul id="tabableNav">
	<li class="on"><a href="#tab1">Tab One</a></li>
	<li><a href="#tab2">Tab Two</a></li>
	<li><a href="#tab3">Tab Three</a></li>
</ul>

The id="tabableNav" marks this blocks links as being tabular links.

The class="on" distinguishes which tab is currently active.


<div id="tabableContent">

	<div id="tab1" class="switched on">
		tab 1 content
	</div>

	<div id="tab2" class="switched">
		tab 2 content
	</div>

	<div id="tab3" class="switched">
		tab 3 content
	</div>

</div>

The id="tabableContent" marks the contents of this block as being associated to the tabular links.

The class="switched" hides inactive tab blocks.

The class="on" displays which tab is currently active.

The scripting

The script is presented in a closure to prevent variables interfering elsewhere.


var accessibleTabs=function(){

  var tabbableNavId="tabableNav",// Id of the link block
      tabbableContentId="tabableContent",// Id of the content blocks container
      onClass="on";// Class name used to indicate active link and content block

  /* author: Simon Willisons - http://simonwillison.net/2004/May/26/addLoadEvent/ */
  function addLoadEvent(f){var o=window.onload;if(typeof window.onload!='function'){window.onload=f;}else{window.onload=function(){if(o){o();}f();};}}

  function stripAnchorFromURI(uri){
    return uri.slice(uri.lastIndexOf('#')+1); // return just the last anchor of a URI address
  }

  function tabClicked(){ // Called when a "tabableNav" link is clicked.

    var tabId=stripAnchorFromURI(this.href);
    if (tabId.length){
      // remove "on" class from all the tabNav li's
      var lis=document.getElementById(tabbableNavId).getElementsByTagName('li');
      if (lis){
        for (var j=0;j<lis.length;j++){
          lis[j].className=lis[j].className.replace(onClass,'');
        }
      }
      // add "on" class to the li containing the clicked anchor
      this.parentNode.className=this.parentNode.className+" "+onClass;

      // remove "on" class from all the content blocks
      var tabContent=document.getElementById(tabbableContentId);
      if (tabContent){
        var divs=tabContent.getElementsByTagName('div');
        if (divs){
          for (var k=0;k<divs.length;k++){
            divs[k].className=divs[k].className.replace(onClass,'');
          }
        }
      }
      // add "on" class to the specifically referenced div content block
      document.getElementById(tabId).className=document.getElementById(tabId).className+" "+onClass;

    }
    return false;
  }

  function init(){
    // do nothing until the page has loaded
    addLoadEvent(function(){
      // work through each link found in the tab nav block
      var tabNav=document.getElementById(tabbableNavId);
      if (tabNav){
        var As=tabNav.getElementsByTagName('a');
        if (As){
          for (var i=0; i<As.length;i++){
            // Add an onclick event calling the tabclicked function
            As[i].onclick=tabClicked;
          }
        }
      }
    });
  }

  return{
    init:init
  };

}();
  

The CSS

The styling requires JavaScript detection so it can act prior to page rendering. Indicated by the class "hasJS" added to the html element. This is achieved by adding a small script directly after the title element:


<script type="text/javascript">/*<![CDATA[*/document.documentElement.className="hasJS";/*]]>*/</script>

Styling required by the scripting only. Nothing cosmetic here:


#tabableContent {position:relative}
.hasJS .switched {position:absolute; top:0; left:-200em;}
.hasJS .on {position:relative; top:0; left:0}

Any issues please email me or leave a comment below.

Have your say…

Say what?

The commenting system used here is a modified version of comment_rave.