@owenshifflett Forget @veryblair, she left me too!

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
  • 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.

  46. G’day, love the script, but have one a question/problem if anyone can help?
    Firstly, works in IE6, IE8, FF & Safari, but on my Nokia N95 mobile phone, it doesn’t. It loads the page, then goes straight back to tab 1. Why and how is this fixable?

  47. Got another question, how can I include content from links within a tab?

    For example, tab 2 has a list of links to the left, and when the user clicks a link, it will display the content.
    This also includes say having a search input box on the page and the user can search a sql db and display the records beside the menu in say tab 2.

    I’m hoping this is possible, because if so, this is the ultimate script for what I need.

    Any/all responses would be greatly appreciated.
    Cheers :)

  48. @Patrick-
    Sorry, not sure about the specs on your phone, so I can’t really help to debug the problem.

    In regards to your other question, anything is possible. You will just need to create that functionality. You’ve got divs to do whatever you want with.

  49. Excellent script Trevor! And thanks for trying to teach a little while sharing it at the same time.

    I’ve tried to implement it myself and have it all working but due to having three tabbed navs on one page I’m having trouble with one thing. The example is here: http://dev.creativelogic.org/aboutus/the-team

    What is happening is on the bottom tab in particular when you click a link the page jumps back to the top of the first tabbed nav. I thought it was a simple thing such as the anchor links causing it but no matter what I did to the links in the tabs in that section it always went back to the top. Is it something in the javascript I need to alter then? Thanks in advance!

  50. @Trevor Robertson-
    I think it’s because you are at the bottom of the page, and there isn’t much room below last set of tabs. So for that split second while the tabs are switching, the height of the page is changing.

    You could try scrolling the window to the top of the tabs using a plugin.

  51. Ah, that makes sense. I will have to try that out. Thanks, I really appreciate the help!

  52. ahh please can someone delet my comment here?

    how to post a code here?

    thanx and sorry again…

  53. @arpix-
    Wrap your code in <pre><code> tags. Then, make sure to replace all < with &lt;

  54. I just wanted to thank you for your hard work.

  55. Is there any way to make the top level tab click simply go to the link provided instead of running the .click function?

    So when I click tab 1 I want it to go to the URL listed. When I click tab 2 I want it to change the page content as normal.

    Anyway to do this?

  56. @Mark-
    You can just assign a class to the URLs that you don’t want to change the tab. Then, take that class into account in the JavaScript to filter it out.

  57. Ya I tried to do that. It’s included in the “ul.tabNav a” area. I tried writing an empty function for it, but that didn’t work. How would you filter it out? Only way I can think of is to make the other ones a certain class and put that into the javascript function already there.

  58. @Mark-
    Try using the :not selector.

  59. Got it to work. For anyone else of the same desire:

    $(‘ul.tabNav :not(#nav_home) a’).click(function()

    Space before :not and after the ending parentheses.

    • goodbyeplanet
    • 2.3.2010 at 4:15 am
    • #
    • Reply »

    thanks for the nice tutorial, this is definitely what I have been looking for. My question for now is what if I am using IDs instead of classes. how do i go about it

  60. @goodbyeplanet-
    Just change the class selectors to id selectors.

    • goodbyeplanet
    • 2.3.2010 at 11:18 am
    • #
    • Reply »

    @Trevor-
    thank you Trevor, I have no problem changing my class selectors to id selectors in CSS but how do i make the change in the javascript. instead of using .removeClass(‘current’); how do i change it to reflect the id part. for example in this javascript which has the class current:

    $(this).parent().parent().children(‘.current’).removeClass(‘current’);
    $(this).addClass(‘current’);

    i changed the above to:

    $(this).parent().parent().children(‘#current’).removeid(‘current’);
    $(this).addid(‘current’);

    but the moment i make those changes nothing seems to work.

  61. @goodbyeplanet-
    Those are not methods in jQuery. You could use attribute methods though.

    • goodbyeplanet
    • 2.4.2010 at 11:29 am
    • #
    • Reply »

    @Trevor-
    thank you Trevor for your help. I ended up combining ids and classes and that did the trick. But I have two observations :

    1). the class tabnav on seems to work even if i dont declare it in my css. how is this possible.

    2). what does this script do:
    i noticed that the toggling will not work without that.

    • goodbyeplanet
    • 2.4.2010 at 11:32 am
    • #
    • Reply »

    thank you Trevor for your help. I ended up combining ids and classes and that did the trick. But I have two observations :

    1). the class tabnav on seems to work even if i dont declare it in my css. how is this possible.

    2). what does this script do:

    i noticed that the toggling will not work without that.

    • goodbyeplanet
    • 2.4.2010 at 11:35 am
    • #
    • Reply »

    my apologies Trevor for messing up the board by sending double messages. was trying to send the code but not sure why its giving me blanks even if it try to use pre code.

    anywayz was asking what does the script jquery dot js do. I noticed that the toggling will not work without it.

  62. @goodbyeplanet-
    You have to make sure you encode your < symbols with &lt; when pasting code. jQuery is the JavaScript library being used.

    • goodbyeplanet
    • 2.4.2010 at 1:20 pm
    • #
    • Reply »

    thank you Trevor all is clear now….this was indeed a lifesaver…

    • 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
    • #
    • 8.17.2009 at 6:45 am
    • #
    • 11.22.2009 at 5:04 pm
    • #
    • 11.24.2009 at 9:34 pm
    • #
    • 12.15.2009 at 8:54 pm
    • #

Speak Your Mind

* Denotes Required Field

  1. 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 Arlington, VA. I work full-time at Viget Labs and freelance on the side.

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

Recent Work

  • 4th District IBEW Health Fund
  • Employers Council on Flexible Compensation
  • 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
See More of My Work

Asides