Grow your CSS skills. Land your dream job.

Organic Tabs

Published by Chris Coyier

This article was originally published on October 27, 2009 and is now being updated to 1) be turned into a jQuery plugin 2) have multiple demos on one page 3) utilize jQuery event delegation and 4) prevent animation queuing.
Edited again on June 13, 2011 to use jQuery 1.6.1 and HTML5

Have you ever seen a tabbed content area in a sidebar that was a little "jerky"? The jerkiness can be caused by a bunch of things, like the content in the tabbed areas are of different heights, or maybe the way the switch happens the current one is hidden for a brief second before the new one shows up and the content below it jumps up and back down quickly. For lack of a better term, I'm calling tabs that behave more smoothly organic tabs .

 

View Demo   Download Files

The Plan

The plan is to build a tabbed area, something pretty simple to do from scratch with jQuery, and then make it behave better. Of course, we'll keep it simple, and keep the markup clean and semantic. The guts of the functionality will be based on calculating heights and animating between those heights on the fly.

Since it's conceivable that one could want multiple tabbed areas on a page, we'll make this a jQuery plugin so it can easily be called upon multiple elements.

The HTML

First we will have a wrapper element that will contain the entire tabbed content area. This nicely contains everything, as well as provides a nice target for the jQuery plugin. Inside we will have one unordered list for the tabs (navigation) themselves. These tabs have href attributes equal to the ID's of the unordered lists below that they relate to. The content of the tabs is another wrapper div with a class of "list-wrap". Each of the "panels" is an unordered list. They key here is really the "list-wrap", which will ultimately provide us a good target for setting and animating the height of the content.

<div id="example-one">
			
    <ul class="nav">
                <li class="nav-one"><a href="#featured" class="current">Featured</a></li>
                <li class="nav-two"><a href="#core">Core</a></li>
                <li class="nav-three"><a href="#jquerytuts">jQuery</a></li>
                <li class="nav-four last"><a href="#classics">Classics</a></li>
    </ul>
	
    <div class="list-wrap">
	
		<ul id="featured">
			<li>Stuff in here!</li>
		</ul>
		 
		 <ul id="core" class="hide">
			<li>Stuff in here!</li>
		 </ul>
		 
		 <ul id="jquerytuts" class="hide">
			<li>Stuff in here!</li>
		 </ul>
		 
		 <ul id="classics" class="hide">
			<li>Stuff in here!</li>
		 </ul>
		 
    </div> <!-- END List Wrap -->
 
 </div> <!-- END Organic Tabs (Example One) -->

The CSS

There isn't much trickery here, just setting things up to look right. Very little of this is "required" for the plugin/technique to work, so feel free to rock your own styling here. My first demo is just a horizontal row of tabs, each of which has it's own special rollover color.

/* Specific to example one */

#example-one { background: #eee; padding: 10px; margin: 0 0 15px 0; -moz-box-shadow: 0 0 5px #666; -webkit-box-shadow: 0 0 5px #666; }

#example-one .nav { overflow: hidden; margin: 0 0 10px 0; }
#example-one .nav li { width: 97px; float: left; margin: 0 10px 0 0; }
#example-one .nav li.last { margin-right: 0; }
#example-one .nav li a { display: block; padding: 5px; background: #959290; color: white; font-size: 10px; text-align: center; border: 0; }
#example-one .nav li a:hover { background-color: #111; }

#example-one ul { list-style: none; }
#example-one ul li a { display: block; border-bottom: 1px solid #666; padding: 4px; color: #666; }
#example-one ul li a:hover, #example-one ul li a:focus { background: #fe4902; color: white; }
#example-one ul li:last-child a { border: none; }

#example-one li.nav-one a.current, ul.featured li a:hover { background-color: #0575f4; color: white; }
#example-one li.nav-two a.current, ul.core li a:hover { background-color: #d30000; color: white; }
#example-one li.nav-three a.current, ul.jquerytuts li a:hover { background-color: #8d01b0; color: white; }
#example-one li.nav-four a.current, ul.classics li a:hover { background-color: #FE4902; color: white; }

There is one generically useful technique we are employing though. We need to hide all the content panels except the default one. There are many ways we could go about it. We could use display: none on them in the CSS. We could use the .hide() function in the JavaScript. Both of those ways have shortcomings. Hiding in the CSS means accessibility problems. Hiding in the JavaScript means the panels could potentially be briefly shown when the page loads and then disappear awkwardly. Instead, we can use a combination of both.

/* Generic Utility */
.hide { position: absolute; top: -9999px; left: -9999px; }

Then check in the jQuery plugin code below, we'll reset these values back to "normal" and hide them with JavaScript, so they are ready to be displayed when needed (not kicked way off the page).

The jQuery

This is the plan in plain English for our plugin:

  1. When the plugin in called on an element...
  2. Move the hidden content back to their normal locations
  3. When a "tab" is clicked...
  4. If it's not already the current tab...
  5. ... and nothing is currently being animated...
  6. Set the outer wrapper to a set height of the current content
  7. Set highlighting of tab to correct tab
  8. Fade out current content
  9. Fade in current content
  10. Animate height of outer wrapper to height of new content
(function($) {

    $.organicTabs = function(el, options) {
    
        var base = this;
        base.$el = $(el);
        base.$nav = base.$el.find(".nav");
                
        base.init = function() {
        
            base.options = $.extend({},$.organicTabs.defaultOptions, options);
            
            // Accessible hiding fix
            $(".hide").css({
                "position": "relative",
                "top": 0,
                "left": 0,
                "display": "none"
            }); 
            
            base.$nav.delegate("li > a", "click", function() {
            
                // Figure out current list via CSS class
                var curList = base.$el.find("a.current").attr("href").substring(1),
                
                // List moving to
                    $newList = $(this),
                    
                // Figure out ID of new list
                    listID = $newList.attr("href").substring(1),
                
                // Set outer wrapper height to (static) height of current inner list
                    $allListWrap = base.$el.find(".list-wrap"),
                    curListHeight = $allListWrap.height();
                $allListWrap.height(curListHeight);
                                        
                if ((listID != curList) && ( base.$el.find(":animated").length == 0)) {
                                            
                    // Fade out current list
                    base.$el.find("#"+curList).fadeOut(base.options.speed, function() {
                        
                        // Fade in new list on callback
                        base.$el.find("#"+listID).fadeIn(base.options.speed);
                        
                        // Adjust outer wrapper to fit new list snuggly
                        var newHeight = base.$el.find("#"+listID).height();
                        $allListWrap.animate({
                            height: newHeight
                        });
                        
                        // Remove highlighting - Add to just-clicked tab
                        base.$el.find(".nav li a").removeClass("current");
                        $newList.addClass("current");
                            
                    });
                    
                }   
                
                // Don't behave like a regular link
                // Stop propegation and bubbling
                return false;
            });
            
        };
        base.init();
    };
    
    $.organicTabs.defaultOptions = {
        "speed": 300
    };
    
    $.fn.organicTabs = function(options) {
        return this.each(function() {
            (new $.organicTabs(this, options));
        });
    };
    
})(jQuery);

Using the plugin

Like any other plugin, you'll need to load the jQuery library first, then the plugin file, then call the plugin.

<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js'></script>
<script type="text/javascript" src="js/organictabs.jquery.js"></script>
<script type='text/javascript'>
    $(function() {

        $("#example-one").organicTabs();
        
        $("#example-two").organicTabs({
            "speed": 200
        });

    });
</script>

You can call the plugin without any parameters, or pass in "speed" to adjust the fade out / fade in animations.

Enjoy! And remember, the idea is that you can take this, hack it to pieces, do whatever you want with it, preferably become rich and famous.

 

View Demo   Download Files

 

Comments

  1. Khalid
    Permalink to comment#

    Nice tutorial!

  2. Permalink to comment#

    Great Article! Simple and elegant. The smoothness of it gives it a polished off look.

    Thanks again

  3. Very nice jQuery-Tutorial Chris!

  4. Krinkle
    Permalink to comment#

    This is an awesome piece of work you got !
    Simple but very nice ! Makes it look really good .

    Digg:
    Digg This!

    Retweet (fixed):
    Retweet Me

    PS: Chris: You should really get some social buttons on these posts :P

    • Thanks, but I dislike social buttons. I don’t feel like going off on a big thing about it right now though.

    • Sounds like an article to me ;)

    • LOL… you are such a purist :)

    • Permalink to comment#

      It’s great to read that you don’t like the social buttons. They’re such a mess. Remember when articles were about the content, and not about spreading it around everywhere? Things like that should be organic, imho.

    • “Remember when articles were about the content, and not about spreading it around everywhere?”

      I’m not big on the buttons either, but doesn’t a good idea deserve to be spread to a broad audience?

      Is it more “organic” to email the link to a few friends rather than sharing the link on twitter?

  5. Permalink to comment#

    thanks for the tut!!!!!!

  6. nice tut. I’ve been using DOMtabs and I never really did care for them too much as it seemed like a pain to style properly.

    This, on the other hand, seems great. Will be using it in a project I’m designing for a client today actually. THANKS :)

  7. Looks great. The effect is too slow though.

  8. Permalink to comment#

    Shmooove.

  9. Jacek
    Permalink to comment#

    Question, is there any possibility to navigate to the tab from outside ul, let’s say having a link which will open the page on 3rd tab directly.

  10. Everytime I will be searching for themes with tabs as it provides more content in less space. If I have known this earlier I have made one. Thanks I will include it in my theme myselves hereafter

  11. Permalink to comment#

    Thanks, great tut! Super useful for wordpress sidebars…

  12. I was just looking for a nice side tab code actually, might give this one a go, looks easy enough to edit and works really nice.

    How hard would it be to incorporate a drop down into some of the list items? Im only just learning jQuery and would probably end up with something really messed up if I tried!

  13. Very cool :-)

  14. Jeff
    Permalink to comment#

    Could this be placed in a WP Page or Article?

  15. Really beautiful! Very much enjoy your posts!

    Quick question: How is jquery with seo? Meaning, the hidden columns, can a crawler-bot pic them up?

  16. This is really creative..!

  17. Bas
    Permalink to comment#

    nice, but if javascript is disabled it is not very useful

    second thing I noticed is when you click on two tabs fast (if you don’t wait for animation to finish) you’ll get two tabs open and it is a mess

  18. Brilliant! Thanks a lot bro. I’m going to play w/ it tonight. Tee-hee. ;-)

  19. Jacob
    Permalink to comment#

    Many thanks for the tutorial! I was just looking at your “Explore” section the other day trying to figure out how you did this. I was trying (and failing miserably) at building this myself.
    I must say, I’m a little slow when it comes to understanding the jQuery. A little more explanation would be super useful… especially in a subsequent screencast!

    cheers,
    Jacob

  20. Not a clone of this tutorial, but last month i’ve built and use a similar tab system for my portfolio’s mobile version at http://bcinarli.com/mobile, inspired from page height movement at css-tricks mobile version (http://css-tricks.com/i)

  21. Permalink to comment#

    Really really nice and usefull tutorial.. a fantastic way to don’t create “jerky” tabs =)

    I think I’ll remember this :D

  22. Aaron
    Permalink to comment#

    Perfect timing, was about to start dealing fixing this exact issue with one of my projects!

  23. Junia
    Permalink to comment#

    Good. Thanks for share.

  24. Permalink to comment#

    Chris, this is a really great tutorial and I’m always impressed about your creativity.
    Thx, Michael

  25. Karl Oakes
    Permalink to comment#

    Chris, I am confused by your code, or in partcular your comment, i.e.
    // Set outer wrapper height to height of current inner list
    var curListHeight = $("#all-list-wrap").height();
    $("#all-list-wrap").height(curListHeight);
    To me the code above isn’t doing anything, surely it should be…
    var curListHeight = $("ul#"+curList).height();
    $("#all-list-wrap").height(curListHeight);
    Also do you really need to collect height, as the animate will change the height of the div to the new ul height. Maybe it is just me, not understanding the code correctly. Nice effect though..

    • I know that seems weird, but what is happening there is that a height is literally SET on the outer wrapper. That way when the inside content changes, and say it’s taller, the wrapper wont “jerk” abruptly larger because it has a set height on it preventing it from doing that.

    • Karl Oakes
      Permalink to comment#

      Ahh… right, that makes more sense, force the height of the div so that it will perform the height animation from a guaranteed starting point. Thanks Chris..

  26. Permalink to comment#

    Chris,

    I like the look of this. Is there a way to get some functionality for people who have javascript turned off?

  27. Permalink to comment#

    Oh wow. That is nice. I am definitely going to find a use for this. I had been avoiding something like that because I thought it was so jerky before.

    I’m also using the Anything Slider which is awesome.

    Thanks!

  28. awsome! going to check it right away!

  29. Permalink to comment#

    Very nice solution!

  30. tarek
    Permalink to comment#

    thank’s chris for the nice tut.
    what about tabs in witch the content takes the same color as the selected tab ,what’s the trick in this plz??

  31. Great article as always chris

  32. Permalink to comment#

    Nicely done Chris but…

    I would like to see a stop() on the previously animation when you click on a new tab, test to click fast between the tabs and you will see that it bugs out (tested in FF 3.5.4 mac os). And the possibility do deep link to a tab by a hash (e.g. #tabOne) would have been nice. Otherwise it looks good I think!

  33. John Pisello
    Permalink to comment#

    Cool approach, as usual. I know you like your “rel” attributes, but I’m wondering why you didn’t just use the “href” attribute to point to the individual lists from the nav links? Doing so would provide three benefits:

    1) Users without JavaScript would still be able to use the navigation links (clicking them would scroll the page to the appropriate list). This is the essence of progressive enhancement (and graceful degradation).
    2) You wouldn’t have to prepend the hash mark at various places in your code–you’d already have it from the href attribute value.
    3) You wouldn’t be using the rel attribute in a non-standard way (I know, not much of a gain, but…).

    Am I missing something?

    • Yep, that’s absolutely how you should do it. In this case the rel thing is kinda dumb. I think it was a relic of the original version I used somewhere were ID’s were not going to be a possibility for whatever reason.

  34. Monika
    Permalink to comment#

    Really nice job but is there any way to make these tabs still work with js turned off.

  35. Permalink to comment#

    This is really a nice little tut, will definitly use in an upcoming project. Thank you!

  36. congrats! really cool. (= im trying to adapt to use 2 divs inside of the animation, one at time.

  37. Permalink to comment#

    Hey,
    Great stuff !

    Just wondering…
    Did anyone made any testing of this in IE 6 and 7 ?

  38. Permalink to comment#

    Hey, i loved it!!!
    Can you help me making this work from an outside menu??

    Thanks!!!

  39. wajira
    Permalink to comment#

    great article.

    Can you explain how can use it for blogger.

    Thx…

  40. Permalink to comment#

    Thanks for the article, Chris.

    This was definitely food for thought for the current site I’m making.

    @John Pisello, good call on using the href attribute instead of using rel. That graceful degredation can be everything to some of our clients!

  41. Kenan
    Permalink to comment#

    Could this code be reusable?
    I have many tabs so i don’t want to creaty to many functions for same thing

    Thanks in advance

  42. Very nice slider! I found it doesn’t work right in IE6 though. Any suggestions?

    thanks!

    • It was very good browser, lived and ruled longer than any other before and after it. Now put it to rest in its bed … and smother it with a pillow…

    • Hey Elliott, please forget ie6 now. Why you force other fellow developers too to support ie6. IE6 is dead. IE6 is dead. IE6 is dead. IE6 is dead. Not even supported by the guys who made it. Microsoft web development engineers. Got it?

  43. very nice tab thanks lot

  44. Jason Andreoni

    Thanks for the update, Chris! One thing I was having trouble with in the last version was I was getting some herky-jerky transition action when I tried to float items for side-by-side lists in the tabs.

    I played around in this new demo with Firebug (floated the UL, made it narrower, duped it, floated that one right so they were side by side), and the transitions stayed nice and smooth. I’ll implement these changes tonight in my site.

    *fingers crossed!

  45. hey guys can someone explain how the whole tab system actually pulls in the content?

    i understand when you write a post in wordpress you can give it a category like “featured” or whatever, but how are you getting each section of the tabbed box to pull in the articles headers and display them?

    am i getting the wrong end of the stick on this one? I would be really grateful if someone can explain it to me. thanks!!!

  46. Man, I really like this menu. Unfortunately, as others have noted, it doesn’t work with JS disabled, so I can’t see a way I could use this. I have almost no JS skills, so I won’t be the one to recode this to degrade as per John Pisello’s idea or anyone else’s, but if Chris or someone redoes this to degrade, I have a great use for it in mind.

  47. This code must uses for many WordPress Theme Magazine style ;)

  48. anlatım için teşekkürler (Thank you for your expression)

  49. Seriously Chris, thank you for this. I’ve created a tabs/sliders/accordions in the past only to see them ‘jump’ depending on the height of my browser window. Every time I’ve scrapped the feature. Thank you for solving this!

  50. dave

    This is great, thanks for putting it together… please forgive a question from a jQuery n00b: Can this be easily modified for a fixed height content area instead of one that flexes? Organic Tabs worked for me in WP out of the box, much easier even than others I’ve found that don’t have flexible heights, so I’m wondering if there’s a way to easily just ‘turn off’ the flexible part and keep the fade in/out functionality. Thanks.

  51. This is a great refresher of an already existing jQuery solution. Way to make it more sound with delegation Chris.

    Any thoughts on adding another plugin parameter? Like direction for instance to have the div grow horizontally vs. vertically?

  52. Hi…
    I would love to have an organic tabs in my website…

    But I dont know how to do it… Where Should I put the files…???

    Help…

  53. Dalibor Sojic

    It would be great to have option “collapsible”… so if click on opened tab…. to collapse it

  54. nice plug-in…
    thanks for share

  55. wish i knew any of this..Thanks:)

  56. Nice One, Chris!

  57. You have made a great tool. Can you explain how i can use it on other items then lists. For example on AREA items on an image? THNX!

  58. Thanks again for a very good tutorial, it really helps !
    Just one question :how would you modify it to have all tabs closed when page loads instead of having one tab (current) open ??
    Thanks again

  59. Excited this was turned into a JQuery plugin! Thanks for updating! Tabs make space easy to manage!

  60. It’s cool when you turned it into a jQuery plugin. I’ve followed your instructions to create tabs in my websites, and now it’s easier to create just the markup and use the plugin. Thanks for sharing.

  61. JECCI

    Very nice but when I add a float element within it gets very jerky. Say you wanted columns instead of rows, with floating left, the smooth transitions disappear..? Any solutions besides using a table? Position:absolute doesn’t seem to work either..?

    • Jason Andreoni

      Hey, JECCI–

      I’m having the same problem. I’m trying to implement on a site I’m working on, but I did a quick example with the downloaded files from the tut and put it up on my testing server to demonstrate the issue.

      http://testing.jasonandreoni.com/testTabs/

      I hope someone can help!

    • The list item that contains the two floated areas has a height of zero. You’ll need to clear the float on that list item so it has height.

    • Jason Andreoni

      Awesome, thank you Chris!

    • JECCI

      Mucho Gracias Chris! I added a clear:left to the end of UL and it work great! Thanks…

  62. Stephanie

    What would be the best way of converting this tabbed menu into a vertical menu having the “#nav” once clicked have the content slide open underneath it?

    I’ve tried to assimilate this, but my tabs won’t stay open, due to something not working with the ‘current’ class

    ta
    Stephanie

  63. Awesome stuff. I have always wondered how to make it that smooth. It is a great one man, thanks.

  64. Worked great! Thank you!

  65. Ganesh
    Permalink to comment#

    Hi,
    First of all, I loved the tab script. It is simple superb.

    There seems to be a small issue with the tabs. I’m using this in an application where i have a child table inside one of the tabs. In that table, I have JS code to add new rows. Since we are calculating static height for each tab, it does not increase the height when i add new row to my child table. Can you tell me how i can fix this?

  66. Thought it may be of interest to readers, I wrote a tutorial for how to implement this into the Thesis framework. Here’s a link to it: thesis-blogs.com.

  67. Desha
    Permalink to comment#

    I’m recieving a firebug error(base.$nav.delegate is not a function) when I use this code in wordpress. It works fine when running locally.

    It’s loading jquery and making the initial .hide css changes but won’t run the function.

    Does anyone have any ideas.

  68. THanks for the organic Tabs! This my Modified Version of it for our investigatory project

  69. bekam
    Permalink to comment#

    did this have another version that support jQuery at least 1.9.x. i tested this with jQuery 1.10.2 but, nothing functional. another question how to make this still functional with callback function if javascript off.
    i tried this http://css-tricks.com/functional-css-tabs-revisited/ before but height adjustment of the content make me frustrated.

  70. David
    Permalink to comment#

    Hi. Thanks for this useful code! I would like to know if there’s a way to link to another tab inside the current tab…

    Like an anchor or something. I.e.: I have some text in the tab 1, and it reads: “Click here to go to 2nd tab”… and when I click it, it goes to tab 2…
    Do you know if this is possible? and how to implement it?

  71. ankit sharma
    Permalink to comment#

    hii guys i’ll need some help for u…
    I want to make a
    multiple tab like email system.

    I click on Inbox page and read any email that action open in view tab
    and that tab have multiple removable tab.

    soo what should i do

  72. Karen
    Permalink to comment#

    How can I use this in a wordpress page/post?

  73. Krista
    Permalink to comment#

    Hi. Great tutorial. However, for my particular site I would like it so that the accordions are all closed when the page loads. Also, I would like the accordion to close if I click on the title tab again. Any ideas on what code I need to change to get these effects?

  74. Anargath
    Permalink to comment#

    Hey, very nice application! =)

    But, when you click on a tab the first time, the content-area below changes it’s height (flows about 10px upwards).. might this be my fault or are there any others with this problem?

    • solved!
      \#tab_wrapper .list-wrap {
      background: #eee; padding: 10px; margin: 0 0 15px 0;
      height: auto !important; /* <- I just added this */
      }

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".