Pre 4th of July dinner: ribs, chili, baked beans, watermelon....mmmm

jQuery Tabbed Navigation

It seems like more and more these days, JavaScript tabbed interfaces are being used. There are plenty of scripts out there, but I think it is important to be able to do things yourself. A lot of JavaScript “plugins” that you will find online will give you way more than you really need, and you may understand how to use it, but you will not really understand what it is really doing.

First, I thought I would walk through creating a tabbed navigation script with jQuery, and then modifying it so that it works without JavaScript.

If you want to skip ahead, take a look at the sample jQuery tabbed interface.

The Markup

I’m going to set this up using classes so that it is possible to use this multiple times on a page. There are two different pieces to the markup: the navigation and the actual content for the tabs.

The Navigation

<ul class="tabNav">
	<li class="current"><a href="#">Tab 1</a></li>
	<li><a href="#">Tab 2</a></li>
	<li><a href="#">Tab 3</a></li>
	<li><a href="#">Tab 4</a></li>
	<li><a href="#">Tab 5</a></li>
</ul>

Pretty straightforward, just an un-ordered list. The JavaScript will accommodate any number of tabs. Notice that the first tab has a class of current. This will be the class to style to show the active tab.

The Tab Content

<div class="tabContainer">
	<div class="tab current">
		…Content for Tab 1…
	</div>
	<div class="tab">
		…Content for Tab 2…
	</div>
	<div class="tab">
		…Content for Tab 3…
	</div>
	<div class="tab">
		…Content for Tab 4…
	</div>
	<div class="tab">
		…Content for Tab 5…
	</div>
</div>

Again, pretty straightforward: a container and a div for each of the tabs’ content. A couple of things to note: the first tab content also has a class of current, and the tabs’ content divs need to appear in the same order as the navigation.

Some Basic CSS

I’m not going to explain how I completely style my example, but I will highlight the necessary styles for the tabs to work.

To highlight the active tab, this is what you want to style:

ul.tabNav li.current a { … }

In order to hide all of the tabs’ contents except for the active tab, these are the styles that are necessary:

div.tabContainer div.tab { display: none; }
div.tabContainer div.current { display: block; }

The JavaScript

Ok, this is definitely the meat of the post. Since I wanted this to be as generic and simple as possible, I made heavy use of the parent and children selectors.

So to start, we want to add onclick events to the tabs:

$(document).ready(function(){
	$('ul.tabNav a').click(function() {
		…
		return false;
	});
});

When each tab is clicked, we want to see which tab was clicked on so that we can show the appropriate content. So we count the previous number of siblings of the parent (the list item) and store it in the curChildIndex variable:

$(document).ready(function(){
	$('ul.tabNav a').click(function() {
		var curChildIndex = $(this).parent().prevAll().length + 1;
		…
		return false;
	});
});

Next, we want to remove the class of current from the currently selected tab, and add the class of current to the tab we just clicked:

$(document).ready(function(){
	$('ul.tabNav a').click(function() {
		var curChildIndex = $(this).parent().prevAll().length + 1;
		$(this).parent().parent().children('.current').removeClass('current');
		$(this).parent().addClass('current');
		…
		return false;
	});
});

The final step is to hide the tab content that is currently showing and to show the tab content that matches the tab that was just clicked on. This is where we will make use of the curChildIndex variable and the nth Child Selector:

$(document).ready(function(){
	$('ul.tabNav a').click(function() {
		var curChildIndex = $(this).parent().prevAll().length + 1;
		$(this).parent().parent().children('.current').removeClass('current');
		$(this).parent().addClass('current');
		$(this).parent().parent().next('.tabContainer').children('.current').slideUp('fast',function() {
			$(this).removeClass('current');
			$(this).parent().children('div:nth-child('+curChildIndex+')').slideDown('normal',function() {
				$(this).addClass('current');
			});
		});
		return false;
	});
});

The previous snippet of the code is traversing from the clicked tab ,(this), to the current content that has a class of current, sliding that up, and removing the class of current. Once that is complete, we are selecting the tab content that matches the tab clicked, sliding it down, and adding the class of current.

Take a look at the sample jQuery tabbed interface to see it in action.

Making it Work for Non-JavaScript Users

Now, that works great, but what about people who don’t have JavaScript enabled? Sure it’s a very small number, but we still want to account for those people.

Most other JavaScript tab scripts use JavaScript to hide the non-active tab content sections or to write in the tabbed navigation, but then you get that flash of changing content. My idea is to use CSS to hide the non active tab content (which I discussed earlier) and pass a parameter into the URL to select which tab to show.

The Code

Below is the new code for the tab navigation:

<ul class="tabNav">
	<li<?php if(!isset($_GET['tab'])) echo ' class="current"';?>><a href="">Tab 1</a></li>
	<li<?php if(isset($_GET['tab']) && $_GET['tab'] == 2) echo ' class="current"';?>><a href="?tab=2">Tab 2</a></li>
	<li<?php if(isset($_GET['tab']) && $_GET['tab'] == 3) echo ' class="current"';?>><a href="?tab=3">Tab 3</a></li>
	<li<?php if(isset($_GET['tab']) && $_GET['tab'] == 4) echo ' class="current"';?>><a href="?tab=4">Tab 4</a></li>
	<li<?php if(isset($_GET['tab']) && $_GET['tab'] == 5) echo ' class="current"';?>><a href="?tab=5">Tab 5</a></li>
</ul>

Basically, I am checking to see if the variable tab is set in the URL, and determining the value of it. I also use the same logic for the tab content. This is the example page with tab 3 displaying. So basically, if the user does not have JavaScript, it would reload the entire page and display the correct tab.

Conclusion

This was just a simple example of jQuery, and the possibilities are endless. Instead of being so quick to use a plugin or someone’s code, try to do it yourself. You will become a much better programmer because of it. Even if you aren’t doing things as efficiently as possible, you will learn something new every time.

Share This:
  • del.icio.us
  • Digg
  • Twitter
  • Facebook
  • StumbleUpon
  • Google Bookmarks
  • NewsVine
  • Technorati
  • Reddit
  • Design Float
  • LinkedIn

RSS feed of comments for this entry

    • James Cready
    • 8.22.2008 at 1:17 pm
    • #
    • Reply »

    ul.tabNav a { outline: none; }

    Just FYI

  1. @James-
    I was considering removing the outline, but I thought the terrible usability did not outweigh the gain from removing it.

    • KillerSpaz
    • 8.26.2008 at 11:44 am
    • #
    • Reply »

    FYI, if you select another tab before the animation sequence is completed, the tab is selected but the body content is still the previous tab.

  2. Thanks… your tutorial was very easy to follow, and very helpful.

    ~Aaron I

  3. Nice Work and nice info….Thanks for sharing the knoledge. Keep up the good work.

    Jay S

  4. Thanks for taking the time to post this article. I needed to create a simple tab based navigation menu using CSS and JQuery and after browsing about 10 articles I found this one really easy to follow.

    Thanks again for sharing the code.

    Vardan

  5. Hey great article
    But i still remain with some doubts.. I just dont know why the second part doesnt seem to work on my site bemcapaz.net

    They do change the style of the nav but doesnt change the value for the divs, the ‘tab atual’ remains ‘tab atual’ no matter wich option i choose.

    Obs.: atual = current i have renamed it for easier work for me ;)

  6. How could I get it to work without the sliding up and down, and just have it click to the next.

    Kind of like this example: http://www.mirceasoaica.com/demo/tabs.html

    I can get yours to work on my site here for FF3, IE7 and IE6. But I can’t get the example I posted to work on IE7 or IE6.

    Check out your jQuery tabs implemented here, and you’ll see why I won’t be able to use the sliding up and down: http://barefoot-webdesign.com/tsw/cd-stud-welders.html

  7. @Spencer-
    Instead of using the slideUp and slideDown functions, just use hide and show.

  8. Thanks! It now works.

    For those wanting only to show and hide with no effects, here’s the solution. Thanks Trevor for helping me with this quick fix.

    $(document).ready(function(){
    	$('ul.tabNav a').click(function() {
    		var curChildIndex = $(this).parent().prevAll().length + 1;
    		$(this).parent().parent().children('.current').removeClass('current');
    		$(this).parent().addClass('current');
    		$(this).parent().parent().next('.tabContainer').children('.current').show('fast',function() {
    			$(this).removeClass('current');
    			$(this).parent().children('div:nth-child('+curChildIndex+')').hide('fast',function() {
    				$(this).addClass('current');
    			});
    		});
    		return false;
    	});
    });
    • Michael SteelWolf
    • 3.19.2009 at 9:06 pm
    • #
    • Reply »

    Any idea why my content jumps around the screen during the transition on webkit-based browsers (Safari and Chrome)? Your example works fine so I’m clearly doing something wrong in the translation…

  9. @Michael SteelWolf-
    Do you have a link to your example?

    • Michael SteelWolf
    • 3.19.2009 at 9:23 pm
    • #
    • Reply »

    http://mistypedurl.com/devspace/2009/03/once-more-unto-the-breach/

    I’ve been messing with it tonight so it’s actually a bit more broken than normal - but the jumping still occurs in webkit browsers.

  10. @Michael SteelWolf-
    I’m not seeing where the tabs are implemented. I see where it says: “No comments yet”, “0 Trackbacks/Pings” & “0 Tweets”, but nothing happens when I click them.

    • Michael SteelWolf
    • 3.19.2009 at 9:33 pm
    • #
    • Reply »

    Yeah, I completely broke the script now…let my try to put things back how they were.

    • Michael SteelWolf
    • 3.19.2009 at 9:36 pm
    • #
    • Reply »

    Alright, at this point things are the way they were - working in FF and IE (remarkably), but jumping text in Safari/Chrome.

  11. @Michael SteelWolf-
    I would try adding clear: both to div.commentlist-container. It just looks like it isn’t clearing the tabs until it’s fully visible.

    • Michael SteelWolf
    • 3.19.2009 at 9:58 pm
    • #
    • Reply »

    Wow, you’re brilliant. Works like a charm. Thank you so much!

  12. Hey,

    When I try adding the fade effect as a transition between panels, it doesn’t work! I replaced slideup and slidedown with fadein and fadeout. What am i doing wrong?

    Also, this tabbed method doesn’t work unless you have the tabbed menu laid directly on top of the content panels. I wanted to place an banner-sized image between the two—it didn’t work. But that’s ok…I willing to go without it

  13. @STS-
    Not sure, fadeIn and fadeOut should work just fine. Do you have a link to it not working?

    Right, this tab menu is using parents and children to traverse. You would need to change the JS in order to account for a change in the markup.

  14. Nevermind, Trevor..I got the fadeIn/fadeOut to work! yaaay! I had swapped them in the wrong places before: I had to replace slideUp with fadeOut (obviously, to fade out the “current” panel), and replace slideDown with fadeIn!

  15. One more question (so sorry!). When I add the lightbox plugin for JQuery, the tabbed navigation no longer works. Is there some kind of clash? Thanks in advance!

    Great tutorial, Trevor ! .

  16. @STS-
    Nope, there shouldn’s be any issues. Do you have a link to see this problem?

  17. i think this is a great tut for me (beginner style) and works great…

    howerver, my tabs are about one tab, pushed to the right…

    the tab does not line up left with the content…

    CSS is exact…. what am i doing wrong?

  18. @tony-
    I’m not sure what you mean. Do you have a link?

  19. its on my development server, so no…

    Tab 1 seems to be pushed to the right about 50px or so….

    both in IE and FF

    here is a screen shot from FF on a MAC.

    im not worried about the gap between the tabs and container, but more the fact the tab is being pushed right…

    http://www.acnoriega.com/bci/tabbed-menu.gif

  20. @tony-
    Did you remove the margin and padding on the unordered list?

  21. nope.

    CSS is in tact. i only changed the width to fit the center column to 500px.

    and i verified i have no conflicting styles in my CSS sheet… weird… but ill keep digging.

  22. @tony-
    It’s too hard to debug just from a screenshot. If you can just make a dummy page with all of the HTML and CSS in place, it would probably be easier.

  23. How can i put the tabs under the content? i tried to just throw the UL under the content area, and it seems to have killed it… do i need to modify the .js?

  24. @tony-
    Yes, you will need to rework the JS to account for the tabs being after the tab content areas. If you look at the JavaScript, there are multiple pieces that depend on the location in the DOM.

  25. I will take a look and give it a shot. thanks

  26. I got some help from my programming team and can now put the tabs under the content …

    works great for me even testing in IE8 / FF mac.

    if anyone wants it here you go:

    $(document).ready(function(){
    	$('ul.tabNav a').click(function() {
    		var curChildIndex = $(this).parent().prevAll().length + 1;
    		$(this).parent().parent().children('.current').removeClass('current');
    		$(this).parent().addClass('current');
    		$(this).parent().parent().prev('.tabContainer').children('.current').fadeOut('fast',function() {
    			$(this).parent().children('div:nth-child('+curChildIndex+')').fadeIn('normal',function() {
    				$(this).addClass('current');
    			});
    			$(this).removeClass('current');
    		});
    		return false;
    	});
    });
    
  27. Thank you so much for this tutorial!
    I really like the freedom of choosing our own animations.

    Funny thing is happening though when I run my cursor fast over the tabs. Then all the containers show one below the other!

    Any idea how to prevent this from happening?

  28. @Sarah-
    Instead of sliding up the div with a class of current, just slide up all of the divs.

    So instead of this:

    $(this).parent().parent().next('.tabContainer').children('.current').slideUp('fast',function() {

    Do this:

    $(this).parent().parent().next('.tabContainer').children().slideUp('fast',function() {

    That should do it. I didn’t test it, but it should work.

  29. @Trevor-
    I should have mentioned that I run the script on hover and not on click.

    So this is what I have

    $(function(){
    	$('.tabNav a').hover(function() {
    		var curChildIndex = $(this).parent().prevAll().length + 1;
    		$(this).parent().siblings('.selected').removeClass('selected');
    		$(this).parent().addClass('selected');
    		$(this).parent().parent().next('.tabContainer').children('.selected').fadeOut('fast',function()  {
    			$(this).removeClass('selected');
    			$(this).parent().children('div:nth-child('+curChildIndex+')').slideDown('normal',function() {
    				$(this).addClass('selected');
    			});
    		});
    
    	});
    
    });

    What you suggested did not work :(
    Any other help?

  30. Didn’t like how it would trigger the show/hide when you click on a current tab, so I added a check to see if the content tab is visible or not, and only do the show hide when it’s not already visible.

    $('ul.tabNav a').click(function() {
    	var curChildIndex = $(this).parent().prevAll().length + 1;
    	$(this).parent().parent().children('.current').removeClass('current');
    	$(this).parent().addClass('current');
    	if ($(this).parent().parent().next('.tabContainer').children('div:nth-child('+curChildIndex+')').is(':hidden')){
    		$(this).parent().parent().next('.tabContainer').children('.current').hide(100,function() {
    			$(this).removeClass('current');
    			$(this).parent().children('div:nth-child('+curChildIndex+')').show(100,function() {
    				$(this).addClass('current');
    			});
    		});
    	}
    	return false;
    });
  31. @Sarah-
    If you remove the part that is adding selected to the divs, I think that should solve the problem.

  32. @Trevor-
    but if I remove the last line:

    $(this).addClass('selected');

    then the whole thing does not work! The container does not change.

    I really need to find a solution to this. Please?

  33. Sara - can you provide a link to your example?

  34. @nick-
    Sure here:
    http://wpbusiness.sarah-neuber.de/

    it’s a work in progress. The bug appears when you run your cursor over all the services links.

    I’d appreciate it very much if someone could help me solve this problem

  35. I haven’t played with your code yet, but I would try these things:

    Use show/hide instead of the slides and fades. The issue seems to occur when you hover over a new tab before the last one has completed.

    Another thing to try is to put some clode in that will hide all the content tabs. No fade, just immediately set visibility to false.

  36. @nick-
    That was it!
    Thanks so much :)

  37. thank you thank you! I’ve been scouring the interwebs for weeks looking for just this simple solution. My specific need was to have a tabbed style page but have each tab be distinctly linkable.

  38. So I’ve been playing with this for a couple of hours and I can’t seem to find a solution.

    How are you able to have distinct links recognize the corresponding tab. I love how yours work by just appending ?tab=3 to the url and I’m doing the same thing on mine and it doesn’t respond. I noticed your php code, but that doesn’t seem to be helping either.

    Would love some help if you’re available!

  39. @Joelle-
    Do you have a link to your page? Are you having problems getting it to work with or without JavaScript enabled?

  40. Hi, i have it posted here: http://www.penguinsanity.com/test/practices/test2.html

    I can’t get it to work with javascript enabled. The server this is ultimately going on to doesn’t have a php backend, so I’m not able to use your other method.

    Thanks for getting back to me!

  41. @Joelle-
    Hmm, seems to be working fine for me with JavaScript enabled.

  42. @Trevor-
    The interface works perfectly, but I’d like to get the direct links to work. I ultimately want to be able to link to an individual tab from a different page.

    So, ideally, http://www.penguinsanity.com/test/practices/test2.html?tab=2 would open up tab 2 on load. It seems to work perfectly on your version. Is there something in the back end that’s making this work?

  43. @Joelle-
    Yes, you will want to check the variable in the URL and add in conditional statements to determine which tab should show.

  44. @Trevor-
    I’m a n00b to scripting in general. Do you know of any good resources or tutorial for figuring out how to check URL variables? I’m hoping to find a solution that doesn’t require a database.

  45. @Joelle-
    Take a look at this article that I wrote for NETTUTS. It’s the same general principle.

    • 8.19.2008 at 11:55 pm
    • #
    • 8.26.2008 at 4:19 am
    • #
    • 8.30.2008 at 12:53 pm
    • #
    • 1.7.2009 at 6:45 pm
    • #
    • 1.26.2009 at 9:24 pm
    • #
    • 4.21.2009 at 3:25 pm
    • #
    • 6.27.2009 at 6:06 am
    • #

Speak Your Mind

* Denotes Required Field

  1. Sick of filling out this form? Register or Log in now.
  2. To post code snippets, use <pre><code>YOUR CODE HERE</code></pre>
Me, Trevor Davis. My blue steel face…

Hi, I’m Trevor Davis

I’m a 25 year old Front-End Developer living in Alexandria, VA. I work full-time at Matrix Group International and freelance on the side.

I specialize in CSS, HTML, jQuery & WordPress. See more of my work, and then hire me.

Recent Work

  • Monica Davis
  • The National Christmas Tree
  • Matrix Group International
  • The MatriX Files
  • Wireless Career
  • George Washington Wired
  • Direct Selling 411
  • Makeup Bizz
  • InstallNET
  • National Park Foundation
  • Worldwide Breast Cancer
  • Food Marketing Institute
See More of My Work

Asides