Tab Navigation with Smooth Horizontal Sliding Using jQuery

read 40 comments

In this tutorial I'll show you how to create a navigation menu that slides horizontally. It begins with a set of "tabs" on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. While I've never had a need to build one of these for any of my own projects, quite a few people have asked if I would demonstrate how it might be done, so here goes.

Download

You can now download a .zip of complete, working demos featured in this post.

The Styles

For the navigation items, I used a simple unordered list wrapped in a <div class="nav">. While the HTML structure is straightforward, the CSS is a little tricky, so I'll detail it here:

CSS:
  1. /* nav wrapper */
  2. .tab-nav {
  3.   position: relative;
  4.   width: 610px;
  5.   overflow: hidden;
  6.   background: #ddd url(tab-slide.png) no-repeat 0 0;
  7. }
  8. /* nav */
  9. .tab-nav ul {
  10.   position: relative;
  11.   float: left;
  12.   width: 1600px;
  13.   margin-left: 535px;
  14.   padding-left: 0;
  15.   list-style-type: none;
  16.   background-color: #fff;
  17. }
  18. .tab-nav li {
  19.   float: left;
  20.   clear: left;
  21. }
  22.  
  23. .tab-nav a {
  24.   display: block;
  25.   width: 74px;
  26.   border-right: 1px solid #ddd;
  27.   height: 25px;
  28.   line-height: 24px;
  29.   float: left;
  30.   text-align: center;
  31.   text-decoration: none;
  32.   color: #000;
  33.   background:  url(tab-slide.png) no-repeat 2px -194px;
  34. }
  35. .tab-nav a.expanded {
  36.   background-position: 2px -244px;
  37. }
  38. /* second-level overrides */
  39. .tab-nav ul ul {
  40.   float: left;
  41.   background-color: #333;
  42.   width: auto;
  43.   margin-left: 0;
  44. }
  45. .tab-nav li li {clear: none;}
  46. .tab-nav li li a { color: #fff; width: 100px; background-image: none;}

Most of the relevant CSS here has to do with positioning the nav items. I set the top <ul>'s left margin to 75 pixels less than the wrapper's width so that the top-level links appear on the right side. The 1600px width for the <ul> gives the floated list items ample room to line up horizontally next to each other.

The wrapper's overflow declaration is significant, as it hides the list items when they're sticking out to the right, but the rest is "window dressing."

Sliding the Nav

With the nav looking the way I want it at its initial state, it's time to make it do something. I'll start with a simple setup, having each "tab" (top-level item) slide to the left on the first click to reveal its sub-nav items, and slide back to its initial position when it's clicked a second time.

For this basic behavior, everything can be done inside a click handler for the top-level links. Note: Since I'm using multiple navs for this tutorial, each with its own set of behavior, I'll be referring to them by ID, unlike in the CSS snippet above, where everything is styled by class. There is nothing special about the selectors or their naming here. Name your own elements and select them however you want.

The first thing to do is set a few variables. The $parentItem variable is the <li> parent of the clicked link. The slideAmt is the width of the nested <ul>, which is the next sibling of the link. And direction will eventually determine whether the parent <li> should be slid to the left or to the right.

JavaScript:
  1. var $topLinks1 = $('#tab-nav-1 > ul > li > a');
  2. $topLinks1.click(function() {
  3.   var $parentItem = $(this).parent(),
  4.       slideAmt = $(this).next().width(),
  5.       direction;
  6.   // code continues
  7. });

Notice the use of $(this). Inside the click handler, this refers to the clicked DOM element. By wrapping this in $(), we can call jQuery methods on it.

To get the sliding motion to occur, we can animate either the left property or the marginLeft property. Here, I'll animate marginLeft. So, the next thing to do is determine the direction of the animation based on the current value of marginLeft: If it's less than 0, direction is set to "+=", which increases it (back to 0); otherwise, direction is set to "-=". At the same time, an "expanded" class will be toggled so that the arrow background image can change directions.

JavaScript:
  1. var $topLinks1 = $('#tab-nav-1 > ul > li > a');
  2. $topLinks1.click(function() {
  3.   var $parentItem = $(this).parent(),
  4.       slideAmt = $(this).next().width(),
  5.       direction;
  6.  
  7.   if (parseInt($parentItem.css('marginLeft'), 10) <0) {
  8.     direction = '+=';
  9.     $(this).removeClass('expanded');
  10.   } else {
  11.     $(this).addClass('expanded');
  12.     direction = '-=';
  13.   }
  14.   // code continues
  15. });

Finally, we do the animation, plugging in the direction and slideAmt variables. The return false; line stops the default click action from occurring. Here is the finished code for the basic implementation:

JavaScript:
  1. var $topLinks1 = $('#tab-nav-1 > ul > li > a');
  2.   $topLinks1.click(function() {
  3.     var $parentItem = $(this).parent(),
  4.         slideAmt = $(this).next().width(),
  5.         direction;
  6.  
  7.     if (parseInt($parentItem.css('marginLeft'), 10) <0) {
  8.       direction = '+=';
  9.       $(this).removeClass('expanded');
  10.     } else {
  11.       $(this).addClass('expanded');
  12.       direction = '-=';
  13.     }
  14.    
  15.     $parentItem
  16.       .animate({marginLeft: direction + slideAmt}, 400);
  17.     return false;
  18.   });

Give it a try:

One at a Time

That's all well and good, but I don't really care for having more than one row of items expanded at a time. A couple simple modifications will fix that for us:

JavaScript:
  1. $(document).ready(function() {
  2.   var $topLinks2 = $('#tab-nav-2 > ul > li > a');
  3.   $topLinks2.click(function() {
  4.     var $parentItem = $(this).parent(),
  5.         slideAmt = $(this).next().width(),
  6.         direction;
  7.     $topLinks2.removeClass('expanded');
  8.     if (parseInt($parentItem.css('marginLeft'), 10) <0) {
  9.       direction = '+=';
  10.     } else {
  11.       $(this).addClass('expanded');
  12.       direction = '-=';
  13.     }
  14.    
  15.     $parentItem
  16.       .animate({marginLeft: direction + slideAmt}, 400)
  17.         .siblings()
  18.         .animate({marginLeft: '0'}, 150);
  19.     return false;
  20.   });
  21. });

The links are stored in a variable first thing here. Whenever a link is clicked, all links have the "expanded" class removed. Of course, only one, at most, will have that class, but I'm lazy and it's easier to tell all of the links to remove the class than it is to hunt links that have the class first.

The same check is performed to set the direction of the animation. If the clicked link's parent <li> is going to be slid to the left, the link will also get the "expanded" class.

Finally, the clicked link's parent is animated in the direction and number of pixels specified by the variables. But here's the twist: all of that parent's siblings have their marginLeft property animated to 0. Again, I'm taking the lazy route, animating all of the siblings, even though one or none of them will need it.

Here's a demo of the "one at a time" version:

Auto-Collapse

Now that the expanding and collapsing are happening the way I like it, I'll add one more little touch. If the user's mouse leaves the containing <div class="tab-nav">, and stays out for a full second, any expanded list will collapse.

JavaScript:
  1. $(document).ready(function() {
  2.   var closeAll,
  3.       $topLinks3 = $('#tab-nav-3 > ul > li > a');
  4.  
  5.   $('#tab-nav-3 ul ul').css('opacity', '0.5');
  6.   $topLinks3.click(function() {
  7.     var $parentItem = $(this).parent(),
  8.         slideAmt = $(this).next().width(),
  9.         direction;
  10.     $topLinks3.removeClass('expanded');
  11.     if (parseInt($parentItem.css('marginLeft'), 10) <0) {
  12.       direction = '+=';
  13.     } else {
  14.       $(this).addClass('expanded');
  15.       direction = '-=';
  16.     }
  17.     $parentItem
  18.       .animate({marginLeft: direction + slideAmt}, 400)
  19.         .siblings()
  20.         .animate({marginLeft: '0'}, 150);
  21.     return false;
  22.   });
  23.  
  24.   $('#tab-nav-3')
  25.   .mouseleave(function() {
  26.     closeAll = setTimeout(function() {
  27.       $topLinks3.removeClass('expanded')
  28.         .parent().animate({marginLeft: '0'}, 150);
  29.     }, 1000);
  30.  
  31.   })
  32.   .mouseenter(function() {
  33.     clearTimeout(closeAll);
  34.   })
  35.  
  36. });

Line 2 declares a variable that will be used for the setTimeout(), which you can see tucked inside the .mouseleave() method near the bottom of the code. The setTimeout() function has two arguments: the first is an anonymous function that contains code for triggering the collapse of the list items, and the second is the number of milliseconds to wait (1,000 milliseconds) before the first argument (the anonymous function) is executed. The clearTimeout() inside the .mouseenter() method does what its name suggests: it clears the timeout. If the user mouses out of the nav area but then mouses back in before the 1000 milliseconds are up, the timer is stopped and the function will not be executed.

Note: The mouseenter(fn) and mouseleave(fn) shorthand methods are available as of jQuery 1.3. If you're still using jQuery 1.2.6, you can use .bind('mouseenter', fn) and .bind('mouseleave', fn) instead. Or, with just about any version of jQuery, you can use .hover(fn, fn).

Here is the final demo:

There are many ways to do this sort of thing. Just tweak the CSS or change the animation for a completely different experience.

comment feed

40 comments

  1. Impressive! Thanks for sharing.

  2. Overall, looks great. Gets a little wonky when you tab through the links. At first I thought this was designed to handle tab/keyboard navigation. Until I tabbed through them all, and then clicked on the 'opener'. Then it really goes nuts.

  3. what a work really nice job done, nice jquery plugin good job.

  4. cschroeder

    Very nice.
    Check IE 7 compatibility though - tabs are always visible.

  5. I don't think I've ever run across navigation that works this way; I like it! It's another example of doing something that's pretty easy in jQuery but creates a very fresh effect.

  6. William Rouse

    I wished you would publish the full source code, not only the js files. For example I don't see how you manipulate the left and right arrow heads for each of the major list items.

    • Hi William,
      As I mention in the entry ("At the same time, an 'expanded' class will be toggled so that the arrow background image can change directions"), the manipulation of the arrow heads is simply done by adding the "expanded" class to the major list items. The arrow heads are in an image sprite. Adding the class changes the background position. I showed all of the CSS (and commented on some of it) in the first code block above. In any case, here is the relevant CSS for switching the direction of the arrows:

      .tab-nav a {
      /* other style declarations here ... see first code block above */
        background:  url(tab-slide.png) no-repeat 2px -194px;
      }
      .tab-nav a.expanded {
        background-position: 2px -244px;
      }
      /* second-level links */
      .tab-nav li li a { color: #fff; width: 100px; background-image: none;}
      

      Here is the image sprite.

      • William Rouse

        Thank you for you kind response. I really appreciate your tutoring. I will study your work later today when I can spend some time to look at what I don't understand. Take care and once again thanks for your attention.
        WBR

        • William Rouse

          It seems that the problem is IE8. The same code works in Chrome but fails to produce the right pointing arrow in IE8.
          Thanks!
          WBR

  7. interesting functions, a new model of navigation tabs. thanks

  8. Ayo

    Where is the download link to download the full working example :-(

  9. its really wonderful, I am working on a Arabic website and now I can use this one for menu, never thought to do something like this.

  10. That's awesome horizontal sliding navigation. I love it, and I will be perfect if you set no outline for the link. Good job!

  11. Adii

    Cool,Awesome thnx man

  12. dlv

    excellent! perhaps i'm gonna using in my next project

    thanks for sahre this kind of resources !

  13. Muito bom essa aplicação em JQuery
    Very good this aplication in JQuery

    vou até adicionar isso no meu site hihihi
    I going add this into my site hihihi

  14. Cool menu.
    Integrating with blogger I've run into a hitch, the mouse enter event wasn't bound to the tab-nav id:

    
    .mouseenter(function() {
        clearTimeout(closeAll);
      })
    

    so I've added and it looks like this:

    
    $('#tab-nav-3').mouseenter(function() {
        clearTimeout(closeAll);
      })
    

    I understand that this would happen in a normal context, also I didn't test it yet...

    • Alberto,
      You actually caught a little bug in the entry's formatted code. Although the source code is correct, the code I posted in the entry had an extraneous semicolon. You can either add $('#tab-nav-3') to the .mouseenter() line, as you did, or remove the semicolon from the previous line (and rely on chaining). I chose to remove the semicolon.

      Cheers!

  15. Max

    Hi Karl, Great work. one question tough, is it possible to have the menu on the left, with submenu sliding to the right ? I've tried, but didn't get it to work. And another question, is it possible to have the submenu already open and highlighted when arriving on a page ?? thanks a lot

  16. I'am newbie, would u share the HTML for this tutorial ?

    tnx b4

  17. Very informative article.I learn more from this site.Thank you.

  18. interesting functions, a new model of navigation tabs. thanks

  19. Jon H

    It's nice you guys have attached the .js file..but for us total n00bs with JQuery can you guys also post or upload the HTML so we can see how all of this works. I'm very interested in learning JQuery and pulled down the .js file..but any possibility you guys can upload the entire HTML or what calls the JS?

  20. I'm very interested in learning JQuery and pulled down the .js file..but any possibility you guys can upload the entire HTML or what calls the JS?

  21. If you take a look at this link Click here If you look at the three slightly different colored tabs, I am trying to use jquery ui with these tabs. I want the hover effect to work on these tabs so that when I hover over each tab, new information is displayed. I know that jquery ui tabs widget can do this. To be quite honest though, I have no idea where to start to learn how to customize this widget to the size that I want it to be. I am new to javascript, so I was wondering if there is a website where it will somewhat walk me through this process?

    Thanks for any help.

  22. I think i would like to try this great menu in my blog. Thanks

  23. Gethin

    Am trying to get something like this, so far not working any hint? Thanks

    
    
    	           
    
    			    Menubar
    
    
             	 
  24. doug

    Better would be for the button to remain in place while the links expanded out from the button. This would keep the user from having to move the mouse to collapse the links themselves.

  25. hi, ive been looking for something like this to use for my blog (blogger). is there a way that i can use this for blogger? what will the codes look like? this is a very wonderful widget.

  26. mdavidangst

    This script is great! -

    Is there a Version of this where the Menu is on the LEFT SIDE?
    I've been tweaking the code, but it's just not right... If anyone, or the author has this in an opposite side version - that would really be fantastic!

10 Pings

  1. [...] Tab Navigation with Smooth Horizontal Sliding Using jQuery [...]

  2. [...] Tab Navigation with Smooth Horizontal Sliding Using jQuery » Learning jQuery – Tips, Techniqu... (tags: jquery howto) [...]

  3. [...] a website’s performance, and with jQuery, we can easily implement awesome transition effects.Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set [...]

  4. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

  5. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

  6. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

  7. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

  8. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

  9. [...] Tab Navigation with Smooth Horizontal Sliding In this tutorial you will create a navigation menu that slides horizontally. It begins with a set of “tabs” on the right side of a containing element. When clicked, a tab slides to the left to reveal a group of links. Click the tab again, and it slides back. [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.