javascript-tabs

Pure Javascript Tabs with HTML 5 and CSS3

So lots of people have done jQuery tabs or other versions of tabbed navigation but I wanted to update this using modern techniques. So we’re going to use an HTML 5 wrapper document and style the tabs using CSS3. Then in this tutorial I’m going to show you how to create the functionality using only javascript no libraries like jQuery or Mootools. In the second part to the tutorial I will then show you how to create a jQuery plugin with the code we’ve written. Sound good? I hope so.

View DemoDownload Source

The HTML

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Pure Javascript, HTML 5 &amp; CSS3 Tabs</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="wrapper">
  <div id="tabContainer">
    <div class="tabs">
      <ul>
        <li id="tabHeader_1">Page 1</li>
        <li id="tabHeader_2">Page 2</li>
        <li id="tabHeader_3">Page 3</li>
      </ul>
    </div>
    <div class="tabscontent">
      <div class="tabpage" id="tabpage_1">
        <h2>Page 1</h2>
        <p>Pellentesque habitant morbi tristique senectus...</p>
      </div>
      <div class="tabpage" id="tabpage_2">
        <h2>Page 2</h2>
        <p>Pellentesque habitant morbi tristique senectus...</p>
      </div>
      <div class="tabpage" id="tabpage_3">
        <h2>Page 3</h2>
        <p>Pellentesque habitant morbi tristique senectus...</p>
      </div>
    </div>
  </div>
<script src="tabs.js"></script>
</body>
</html>

Nothing new here really, we start with the common HTML5 doctype stuff and a wrapper div just for styling. Then we have the tabsContainer div that does what it says this is the container for the two main tab divs. So first the tabs themselves. These are inside the #tabs div and are contained inside a ul element. Each of the tabs has an id that will be used to target the tabs later.

Next is the tabscontent div this holds three divs (for the three tabs but this could be extended by add more divs here and more tabs to the list above). Each of these has the same class name to be used for styling and a different id which will be used to target the individual divs in the javascript. Then at the bottom I use the lazy HTML5 syntax for bringing in scripts.

The CSS

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
* {
	margin:0;
	padding:0;
}

body {
	background:url(../../images/background.png) top left;
	font: .8em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}

h2{ 
	margin-bottom:10px;
}

#wrapper{
	width:720px;
	margin:40px auto 0;
}

#wrapper h1{
	color:#FFF;
	text-align:center;
	margin-bottom:20px;
}

#wrapper a{
	display:block;
	font-size:1.2em;
	padding-top:20px;
	color:#FFF;
	text-decoration:none;
	text-align:center;
}

#tabContainer {
	width:700px;
	padding:15px;
	background-color:#2e2e2e;
	-moz-border-radius: 4px;
	border-radius: 4px; 
}

.tabs{
	height:30px;
}

.tabs > ul{
	font-size: 1em;
	list-style:none;
}

.tabs > ul > li{
	margin:0 2px 0 0;
	padding:7px 10px;
	display:block;
	float:left;
	color:#FFF;
	-webkit-user-select: none;
	-moz-user-select: none;
	user-select: none;
	-moz-border-radius-topleft: 4px;
	-moz-border-radius-topright: 4px;
	-moz-border-radius-bottomright: 0px;
	-moz-border-radius-bottomleft: 0px;
	border-top-left-radius:4px;
	border-top-right-radius: 4px;
	border-bottom-right-radius: 0px;
	border-bottom-left-radius: 0px; 
	background: #C9C9C9; /* old browsers */
	background: -moz-linear-gradient(top, #0C91EC 0%, #257AB6 100%); /* firefox */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#0C91EC), color-stop(100%,#257AB6)); /* webkit */
}

.tabs > ul > li:hover{
	background: #FFFFFF; /* old browsers */
	background: -moz-linear-gradient(top, #FFFFFF 0%, #F3F3F3 10%, #F3F3F3 50%, #FFFFFF 100%); /* firefox */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(10%,#F3F3F3), color-stop(50%,#F3F3F3), color-stop(100%,#FFFFFF)); /* webkit */
	cursor:pointer;
	color: #333;
}

.tabs > ul > li.tabActiveHeader{
	background: #FFFFFF; /* old browsers */
	background: -moz-linear-gradient(top, #FFFFFF 0%, #F3F3F3 10%, #F3F3F3 50%, #FFFFFF 100%); /* firefox */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(10%,#F3F3F3), color-stop(50%,#F3F3F3), color-stop(100%,#FFFFFF)); /* webkit */
	cursor:pointer;
	color: #333;
}

.tabscontent {
	-moz-border-radius-topleft: 0px;
	-moz-border-radius-topright: 4px;
	-moz-border-radius-bottomright: 4px;
	-moz-border-radius-bottomleft: 4px;
	border-top-left-radius: 0px;
	border-top-right-radius: 4px;
	border-bottom-right-radius: 4px;
	border-bottom-left-radius: 4px; 
	padding:10px 10px 25px;
	background: #FFFFFF; /* old browsers */
	background: -moz-linear-gradient(top, #FFFFFF 0%, #FFFFFF 90%, #e4e9ed 100%); /* firefox */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FFFFFF), color-stop(90%,#FFFFFF), color-stop(100%,#e4e9ed)); /* webkit */
	margin:0;
	color:#333;
}

So forget about all the basic styling and lets start with .tabs. Next we set the tabs ul to have no list-style (to hide the bullet points) set the text size. The next rule is the most complex this is for the li elements (you will notice that I’m using child selectors in my css like the .tabs > ul > li instead of just .tabs ul li this is because I read a document from Mozilla saying that CSS is read right to left instead of left to right so if you specify that the tag is a direct child the css perfoms slightly better. Writing Efficient CSS there are lots of other goodies in there to get your head around (I’m not there yet with it all but I’m getting there).

Ok stop interupting me!! So i set the display:block and float:left to get them to display correctly (I could use display:inline but I just prefer this method). The user-select:none rule (-moz and -webkit are just browser specific variations) makes it so that when you click the text you don’t move the cursor to that part of the page. Then we have the border-radius rules which say we have two curved edges at the top and two straight corners at the bottom. The final part to this is the gradient. Instead of using images (which means more syntax if to want expandable tabs) I’m using CSS3 gradients. I use this great tool Ultimate CSS Gradient Generator which helps you create the gradients visually then copy the code into your css. I’ve deleted the IE specific stuff because the javascript on this technique only works on IE8 and above so IE users will just get what they get. All the other rules in the css use variations of what I just explained. The only other thing to note is that the pseudo class of hover has the same rules as the activeTabHeader which is the style added to the active tab, so when you hover the tab that you are over will display the same as the active tab.

The Pseudo Code

1.
2.
3.
4.
5.
6.
7.
8.
9.
when page loads
  get tab container
  set current tab with class of activetabheader and store which tab we are on
  hide two tab pages we don't need

  on click of one of tabs
  remove class of activetabheader and hide old contents
  add class of activetabheader to new active tab and show contents

So hopefully the pseudo code javascript looks pretty straight forward, on page load we want to get the tab container, set the current active tab and hide the two tab pages we don’t need yet. The we also want to know which tab is clicked and change everything accordingly.

The Javascript

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
window.onload=function() {

  // get tab container
  var container = document.getElementById("tabContainer");
    // set current tab
    var navitem = container.querySelector(".tabs ul li");
    //store which tab we are on
    var ident = navitem.id.split("_")[1];
    navitem.parentNode.setAttribute("data-current",ident);
    //set current tab with class of activetabheader
    navitem.setAttribute("class","tabActiveHeader");

    //hide two tab contents we don't need
    var pages = container.querySelectorAll(".tabpage");
    for (var i = 1; i < pages.length; i++) {
      pages[i].style.display="none";
    }

    //this adds click event to tabs
    var tabs = container.querySelectorAll(".tabs ul li");
    for (var i = 0; i < tabs.length; i++) {
      tabs[i].onclick=displayPage;
    }
}

// on click of one of tabs
function displayPage() {
  var current = this.parentNode.getAttribute("data-current");
  //remove class of activetabheader and hide old contents
  document.getElementById("tabHeader_" + current).removeAttribute("class");
  document.getElementById("tabpage_" + current).style.display="none";

  var ident = this.id.split("_")[1];
  //add class of activetabheader to new active tab and show contents
  this.setAttribute("class","tabActiveHeader");
  document.getElementById("tabpage_" + ident).style.display="block";
  this.parentNode.setAttribute("data-current",ident);
}

So the first function fires on window load. Get the tabContainer div and then query the this container for all the li elements. Next we get the first id and split it where the underscore(_) is and just keep the number (in this case 1). Then we get the li items parent node (ul) and add the HTML5 attribute data-current to it and give it the value of the ident variable(which is 1) this is how we track which tab we are on. Now we give the li the calss of activeTabHeader. The next part puts all the divs with a class of .tabpage in an array and then apart from the first one it adds display:none to the css. (We skip the first one in the array by setting the i variable to 1 instead of the usual 0). then we add a click event to all the tabs so that when they are clicked the displayPage function is called.

The function displayPage then hides the current page and removes the class activeTabHeader from the active tab and adds the class to the tab that has been clicked and changes the css to display:block for the new page.

Thats it all done. Next time we will create a jQuery plugin so we can call this functionality simply on any correctly created tabContainer. We will also give the option of 3 different UI styles.

View DemoDownload Source