jQuery MagicLine Navigation

Posted on: 2/9/2010   By: Chris Coyier 96 Comments

These “sliding” style navigation bars have been around a while, I just thought I’d take a crack at doing it myself as the opportunity came up recently. Turns out it’s really pretty darn easy. I put two examples together for it.

The Idea

The idea is to have a highlight of some kind (a background or an underline) follow you around as you mouse over the different links in the navigation. This will happen with jQuery and it’s animation abilities. As such, the “magic line” will only be appended via JavaScript. Once added to the list and styled, as you mouse over the different links, it figures out the left positioning and the width and animates to match.

HTML

Typical list here…. It has the “group” class because it’s going to be a horizontal row and will need the clearfix so it has height. The ID is for the JavaScript to target it.

<div class="nav-wrap">
 <ul class="group" id="example-one">
    <li class="current_page_item"><a href="#">Home</a></li>
    <li><a href="#">Buy Tickets</a></li>
    <li><a href="#">Group Sales</a></li>
    <li><a href="#">Reviews</a></li>
    <li><a href="#">The Show</a></li>
    <li><a href="#">Videos</a></li>
    <li><a href="#">Photos</a></li>
    <li><a href="#">Magic Shop</a></li>
 </ul>
</div>

Notice the .nav-wrap div around it. That is used because of the styling, the bars go full width of the screen the but the navigation is centered within. This centering brings up an issue when we start the JavaScript.

CSS

Do the ol’ inline list elements with floated left anchors to get the list horizontal and avoid stairstepping. The magic line is absolutely positioned under the bar, so that it doesn’t cause jitter (mouse over a link, animates over, mouse is now on magic line not the link, animates back, etc.) Everything else is purely stylistic.

.nav-wrap { margin: 50px auto;  background-color: rgba(0,0,0,0.6); border-top: 2px solid white; border-bottom: 2px solid white; }
#example-one { margin: 0 auto; list-style: none; position: relative; width: 960px; }
#example-one li { display: inline; }
#example-one li a { color: #bbb; font-size: 14px; display: block; float: left; padding: 6px 10px 4px 10px; text-decoration: none; text-transform: uppercase; }
#example-one li a:hover { color: white; }
#magic-line { position: absolute; bottom: -2px; left: 0; width: 100px; height: 2px; background: #fe4902; }

jQuery JavaScript

  1. When DOM is ready…
  2. Set up variables including current left offset of the navigation
  3. Set up resize function on the window to reset that offset should it change (because of the centering)
  4. Append the magic line to the nav
  5. Set up position and width of the magic line for the current page item
  6. Also set the original width and position as data, so it can be used to animate back to
  7. On hover, calculate the new width and new left position and animate to it
  8. On the hover callback (mouse out), animate back to original

Updated, thanks to smart comments by Daniel and Nora Brown below.

$(function() {

    var $el, leftPos, newWidth,
        $mainNav = $("#example-one");

    $mainNav.append("<li id='magic-line'></li>");
    var $magicLine = $("#magic-line");

    $magicLine
        .width($(".current_page_item").width())
        .css("left", $(".current_page_item a").position().left)
        .data("origLeft", $magicLine.position().left)
        .data("origWidth", $magicLine.width());

    $("#example-one li a").hover(function() {
        $el = $(this);
        leftPos = $el.position().left;
        newWidth = $el.parent().width();
        $magicLine.stop().animate({
            left: leftPos,
            width: newWidth
        });
    }, function() {
        $magicLine.stop().animate({
            left: $magicLine.data("origLeft"),
            width: $magicLine.data("origWidth")
        });
    });
});

Issues

Opera is weird about it. It makes the original width of the magic line the full width of the nav bar and shortens it from the right on hovers. I haven’t been able to figure it out. If you do, awesome, let me know I’ll update this article and demos.

Alternate Version

Check out the demo link below for an alternate version that uses a background instead of a line, and animates color as well as position and width. Basically the same, except the CSS is slightly different and the JavaScript pulls the color of the new list item from a rel attribute in the HTML.

Color animations in jQuery require the color plugin. I was just moaning about how it doesn’t work with RGBa color, and someone sent me a patch that does, which is included in the download. I’m really sorry, but I can’t remember their name! Speak up if it was you and I’ll update this and credit the patch to you.

All Yours

As always, feel free to do whatever you want with it. Preferably, use it in corporate projects and earn wheelbarrows of cash.

View Demo   Download Files

Responses

  1. G says:

    nothing new man…

  2. Hi Chris,
    You make this stuff so simple – thanks for all your great tips and hard work! My question is can this work with multi-level navigation as well? It wouldn’t need to apply the effect the the child links, but would like to be able to have them drop down. Any tips on this is helpful.

    Thanks, Larry

  3. Brandon says:

    Larry just asked the same question that I had as well. This is a great start, but what needs to happen to support multi-level navigation?

    • TeMc says:

      Though I haven’t tried it myself, one thing that might work is duplicate the code for the find(“a”) hover and make one for $(“li:not(:has(ul))”). that does the default stuff (it doesn’t have a UL-child so it’s not a dropdown-able item)

      And another one for $(“li:has(ul)”) that does have a ul-child. In that case move the magic line horizontally where it should be, but also move it down the height of the UL-element.

      If you dont want the magic line to move diagonally (ie. shortest route) then you need to queue the animation to go horizontal first to the LI-item, and then move vertically down (position: absolute; bottom: -(2 + UL-height)px; ). And on MouseOut (callback of hover() – move vertically up and then horizontally).

      I might try this next weekend, but I’m not sure I’ll remember. Feel free to see if it works out !


      TeMc

  4. Try looking into the dropdowns (also jQuery based):
    http://css-tricks.com/simple-jquery-dropdowns/

  5. Peter says:

    Thanks for sharing Chris, just a little detail. Download is broken.

  6. Once again, a sly implementation!

  7. Matt says:

    As always, feel free to do whatever you want with it. Preferably, use it in corporate projects and earn wheelbarrows of cash.

    Yes sir!

    Actually, I needed this for a commercial project as it was, and this just satisfied that need. You’re a 5-star helpful guy :)

  8. Daniele Pignedoli says:

    Wow, simply effect that looks pretty good.

    Just one thing, i prefer to have the magic-line li in the html markup, instead of appending it by javascript.

    Its just a matter of taste and probably your way is better for seo, but as a developer i love to see exactly the markup i’ll have to work with ;)

  9. How would you use this with WordPress’ wp_page_menu?

  10. Slatron says:

    Just what I was looking for today. I like the way you set up all your links to previous examples. I hadn’t checked out your clearfix or stairstepping pages. This was a great way to look at them within a specific context. Thanks agiain, Chris!

  11. Eddie says:

    Is anyone else seeing problems in IE8? The class “current_page_item” color is not working onLoad. I’ve tried setting the color inline and it still doesn’t work. I’m pretty sure it’s a JQuery issue which I don’t know well enough to fix.

  12. Alex says:

    wow, this effect looks really nice and at the same time is very subtle. great work! many thanks for sharing it!

  13. Metin says:

    as always, your work is brillant, yet easy to understand and implement. thanks.

  14. Damian Web says:

    Really nice effect, can’t say i’ve seen it used that often, or i’m just not paying attention to the sites that do use it!

    I have had a look through all the browsers and IE8 is a little funny with it, i’m gunna play around with it tonight and see whats up. As far as opera is concerned… that’s all i have to say on that.

  15. e11world says:

    Very nicely Done Chris! This will be very useful for sites that don’t need dropdowns.

  16. Khaled says:

    Thank you Chris love it!

  17. Jonathan says:

    This is Sexy nav for sure!!! Thanks Chris!!

  18. 3djade says:

    Hi Chris this is really cool, is jquery killing Flash menus? or Animation? I think so.
    Tthere is a very little thing I wanted to Know and I it would be interesting, is it possible to make the magic line stop at the last item on the menu that I clicked?

  19. John says:

    I first viewed it in Opera and thought “awesome”!! I didn’t even realize it was broken in Opera. That just proves (to me at least) that it can look radically different in different browsers, and that’s ok.

  20. timsamoff says:

    I haven’t attempted any debugging, but there’s the strangest thing… Remove the first example from your demo file and the second example stops working. (Unless I did something terribly wrong — which I don’t think I did.)

    • There are some variables set up above /* Example One */ in the JS, make sure those are still there.

    • Jason says:

      Still trying to work with it am having the same problems and am trying to learn all this how can do remove the upper please and still make it work lol
      am a very new guy to type of work and would like to add this style to me new web build for my site.
      cheers Jason

  21. Nora Brown says:

    Just curious if this:
    $("#example-one li").find("a") is better (faster?) than $("#example-one li a") ? Really nice demo – I like these subtle effects you can achieve pretty quickly and simply with jquery.

    • Nope probably not, that’s a blunder on my part. It was original there because I needed to use a .not() filter on some of the list items, but still target the anchor links, hence the .find(). Feel free to write it your way.

  22. Jake says:

    Just added it to a client’s site, Thanks Chris.

  23. Vitor says:

    DUDE U THA BOMB!!!!

  24. Kristijan says:

    One hell of a trick, nice man. Thanks

  25. Great Tutorial. Thanks For Sharing!!

    I love the last one

  26. paul says:

    just put something simmilar into effect on a recent project of mine:
    here

    although I used the lava lamp jquery script

  27. Hmmm.. this is nice. Another thing to play with.

  28. Frank says:

    Been playing a little with it in Opera. Getting the width from the anchor instead of the parent listitem almost fixes it… (Now the problem is that it is a bit too narrow.) Tried tweaking the css to fix that but didn’t quite work either.

    Seems the problem is that .width() of an inline listitem doesn’t work right.

    Reverted my changes and rather changed the css to use inline-block like this:
    #example-one li { display: inline-block; }
    That works perfectly. (in Opera at least. :)

  29. Ted Goas says:

    Chris, great job with this plugin… you’ve taken the Lava Lamp treatment and made it your own. And designed a nice demo page in the process.

  30. Alex Teer says:

    Thks for idea. It’s col

  31. Paul says:

    I’m loving this menu, but finding it very frustrating trying to add sub menus. Has anyone mastered this 2nd part and added sub menus or can point me in the right direction? I’m not a JS guru, so just pointing me to code doesn’t help much, though I’m not a total nitwit when it comes to coding. Any and all help would be appreciated.

  32. pintus says:

    Sorry I split my code…
    just paste both parts!

  33. Joshua says:

    So i am having trouble making this work with wordpress. Works on the homepage but if I go to any other page it fails to work and also never remembers the new page.

    Help :)

  34. Matthias says:

    That’s so simple and sweet. Thanks for sharing! :)

  35. Paul says:

    anyone else not able to get it to stop on the item you last clicked on? the above method doesnt seem to work for me

  36. Federico says:

    Hi, I combined this great script with a vertical submenu…

    http://www.castellolarocchetta.it/test/

    is there a way to make the magic line (i changed it into a little logo) to stay in the original position (current_page_item) instead of moving to the left of the navbar when going on a submenu link? thank you for your great work

  37. Slatron says:

    It looks like the second example doesn’t work for any version of IE. I haven’t gone through the code yet – but has anyone else been noticing this?

    • John Pisello says:

      There is a problem in the jquery.color-RGBa-patch.js code. It’s trying to assign an rgba() value, which IE doesn’t recognize, to the magic line element’s background.

  38. Sonu says:

    Hi Chris, your all tutorials are really awesome. I’ve learnt a lot about CSS and jQuery by just watching and reading ur all articles. They really helped me lot.

    While I’ve a query. Actually I wanna know how u have made this comment system ?

    I mean to say each person can reply to one another too. While the comment box has a very nice feature like, it is showing what to put in the box, and when we mouse over it, it just grayed out but didn’t vanished totally, but when we put our name then it vanishes.

    Can u write an article on this subject ? It will be really helpful for me and like me, who are beginner.

  39. Arvind says:

    This tutorial rock. Thank you.

  40. devlim says:

    set the textarea of this comment “resize” property to “vertical” so when resize it much more good than this

  41. mike says:

    Hey Chris! I was looking at doing something like this just last week! I’m going to give it another shot with this method and try use it for a WordPress theme I’m working on, I’ll try to add the dropdowns too and let you know how I go. Your blog is fast becoming my favorite reference spot for this kinda thing.. cheers :)

  42. Alfredo says:

    Hi! Very great demo. Happy to use on a my client project. I made some test on and I have some trouble under win xp with explorer..Somebosy could help me?
    Cheers:)

  43. Jerm says:

    Hey chris, I didn’t read through all of the comments to see if this was here but i downloaded this and it’s not working in opera, at least not very well.

  44. Alfredo says:

    Hi Chris i need still help for IE.
    http://ekolor.sitiweblecce.it
    I made the changes written up but still doesn’t work.
    Thx:)

  45. Chris says:

    Hi, great script, thanks – but is there a way to put a slight delay on the magicline movement?

    Chris

  46. Vijendra says:

    Hi Chris I’m great fan of your work. Thanks for sharing all such code piece.

  47. LuK says:

    Hey Chris,

    thx for the inspiration again =), the underline-thing in example 1 is great…

    I use this Plugin for several months on different projects and I’m sure this effect could also be achieved with it…the jQuery Code is heavier I think but it really works with all browsers and has very useful additional features…

  48. Nice effect, i like JQuery. Thanks for sharing

  49. Te says:

    Great code!!

    Do you know of a way to make it go vertical rather than horizontal?

  50. haichen says:

    I get example2 working in IE with adding a browser detection in your example.js. After “$(function(){” you can add something like:

    if ($.browser.msie) {
    $('#nav1').attr('rel','rgb(254,73,2) ');
    $('#nav2').attr('rel','rgb(50,69,12) ');
    ...
    }
    else {
    $('#nav1').attr('rel','rgba(254,73,2,0.9) ');
    ...
    };

    In the html you have to add the id=”nav1″ ..
    I also made the changes in the css from John Pisello.

    • Alfredo says:

      Hi Haichen, tried to make your tips. I’m using example ttwo too, applied changes from John P. too, but i still have problems with I.E.
      Site url is ekolor.sitiweblecce.it
      I paste the code too :

      HTML

      <a href="index.php" rel="nofollow">Home</a>
      <a href="chisiamo-ekolor.php" rel="nofollow">Chi siamo</a>
      <a href="dovesiamo-ekolor.php" rel="nofollow">Dove Siamo</a>
      <a href="prodotti-ekolor.php" rel="nofollow">Prodotti</a>
      <a href="novita-ekolor.php" rel="nofollow">Novitá</a>
      <a href="contatti-ekolor.php" rel="nofollow">Contatti</a>

      EXAMLE.JS

      $(function(){
      
          var $el, leftPos, newWidth,
      
              $mainNav2 = $("#example-two");
          

      No changes…
      Thx a lot for your help

    • haichen says:

      If you first load the site you get a “wrong” color. In example.js add the background line to change it.

      $magicLineTwo
      .width($(".current_page_item_two").width())
      .height($mainNav2.height())
      .css("left", $(".current_page_item_two a").position().left)
      .css("background",$(".current_page_item_two a").attr("rel")) ...

  51. Akbar says:

    Thnks Chris. Very useful javascript menu. But it is not perfect in the Ie 6. And i have fix the error. I have used In my menu the jpg image like pointed arrow at the bottom of the menu.

    The page loading in Ie6 the ” magic-line” div is positioned on the middle of the menu text.

  52. marco says:

    hi!
    very nice article.
    is there a way to completely hide the red bar when none of the page is current?
    I mean there are some pages on my app that can’t be reached clicking on the menu.
    When the user is there, the red line should disapper if she is not hovering on the menu.
    any idea?
    thanks!

  53. haichen says:

    I try it again:
    Hi Alfredo,
    The id belongs to your links. They have to look like this:
    <li><a id="nav2" rel="rgb(139,0,139)" href="chisiamo-ekolor.php">Chi siamo</a></li>
    <li><a id="nav3" rel="rgb(255,215,0)" href="dovesiamo-ekolor.php">Dove Siamo</a></li>

    As you can see the links have rgb as standard. If the Browser isn’t IE jquery makes rgba. So Transparency is only for non-IE.

    please delete my previous post
    thx

    • Alfredo says:

      Thx Haichen:)
      Made the changes but still not working..
      Have patience;)
      I paste js code to show u :

      $(function(){
      
          if ($.browser.msie) {
      	    $('#nav1').attr('rel','rgb(128,128,0) ');
              $('#nav2').attr('rel','rgb(139,0,139) ');
      	    $('#nav3').attr('rel','rgb(255,215,0) ');
      	    $('#nav4').attr('rel','rgb(255,165,0) ');
      		$('#nav5').attr('rel','rgb(139,0,0) ');
      		$('#nav6').attr('rel','rgb(0,0,128) ');
      	} else {
      		$('#nav1').attr('rel','rgba(128,128,0,0.9) ');
              $('#nav2').attr('rel','rgba(139,0,139,0.9) ');
      	    $('#nav3').attr('rel','rgba(255,215,0,0.9) ');
      	    $('#nav4').attr('rel','rgba(255,165,0,0.9) ');
      		$('#nav5').attr('rel','rgba(139,0,0,0.9) ');
      		$('#nav6').attr('rel','rgba(0,0,128,0.9) ');
      	};
      
      	var $el, leftPos, newWidth,
      
              $mainNav2 = $("#example-two");

      Thx a lot
      Regards,
      Alfredo

      • haichen says:

        in your css you’ve to put a fallback for the rgba.
        Put both definitions in the css.

        background: rgb(220, 133, 5);
        background: rgba(220, 133, 5, 0.9);

        and this:

        background-color: rgb(0,0,0);
        background-color: rgba(0,0,0,0.6);

        IE takes the rgb and most others the rgba.

        and there are two #nav1, but that seems to be no problem.

        tried this with your code and it worked in IE.
        hopefully this wil help.

      • Alfredo says:

        Thx Master!:))Now all runs fine.U have given me a big help.Thx a lot:)
        Regards,
        Alfredo

      • haichen says:

        I’ve added one more point (“wrong” color) accidently some comments above, but maybe you’ve seen it.
        haichen
        :-)

  54. Kete says:

    Hi!

    I had the problem in Opera but I changed “display:inline” for “float:left” and it worked.

    My HTML code is a bit different so I paste here:

    HTML


    <a href="#" rel="nofollow">Option 1
    <a href="#" rel="nofollow">Option 2
    <a href="#" rel="nofollow">Option 3
    <a href="#" rel="nofollow">Option 4
    <a href="#" rel="nofollow">Option 5

    And the CSS:

    header{
    	overflow:hidden;
    	width:100%
    }
    
    	header ul{
    		border-top: 2px solid white;
    		border-bottom: 2px solid white;
    		clear:left;
    		margin:0 auto;
    		list-style:none;
    		overflow:hidden;
    		position:relative;
    		width: 960px
    	}
    
    		header ul li{
    			float:left
    			/*display:inline;*/
    		}
    
    			header ul li a{
    				color:#51acf7;
    				display:block;
    				float:left;
    				padding:6px 10px 4px 10px;
    				text-decoration:none;
    			}
    
    			header ul li a:hover{
    				color:#999
    			}
    
    		#magic-line{
    			position:absolute;
    			bottom:0px;
    			left:0;
    			width:100px;
    			height:2px;
    			background:#fe4902
    		}

    The js is the same.

    Thanks!

  55. marco says:

    hi! Anyone got example 1 working on IE? thanks

  56. jonmufc says:

    Thanks for sharing this article . I will used your code apply to my project.

  57. Dennis says:

    Is there any possibility to make the menu switch while using other links? For example, I have “news” page on my site (news.html); and I want menu not only to change while I’m pressing news link from the menu, but also casual news link from the main site body. Another problem is that i use IFRAME as main site body. I thought I need something like this:


    <a href="news.html" rel="nofollow">

    This is code inside the IFRAME body; also I have this:

    in the main index page code.

    But it doesen’t work. Note that if I use and alert(‘parent.document.getElementById(‘a1′).className’); I got normally printed message with text “testclass”.

    Probably I have to trigger some additional functionality of the example.js script? How I can do this?

  58. Rob says:

    That’s ridiculously slick. Geez, I want to tear apart my own site now just to implement this style of navigation. Thanks for sharing!!!

  59. Great peice of coding, it’s a massive help to have a CSS jQuery based nav rather than a nasty Flash one!

    Well done Chris!

  60. Jordan says:

    I’m having issues with this in IE8..
    It works in all other browsers that I’ve tested..

    In IE.. when you hover over the link and then mouse out, the line runs away :P
    It goes clear off the page to the right.. creates a horizontal scrollbar.. Ideas?

  61. Johnny says:

    Could this not have been done with a CSS:hover class?

  62. stulk says:

    Awesome menu! I have a contact section and would like the button separted so that it displays on the right. Is this possible? Ive added

    .right_page_item a { float: right; color: white !important; }

    to the css and the webpage, but still no go

  63. Adrian says:

    Hi,

    Great tutorial.

    I had an issue with IE8 in standards mode (was fine in compatibility view though)- getting the initial width of the current_page_item, which actually blanked that item out until I hovered over it. Not great. Seems there’s an issue with getting the width of an inline element in IE8 (although the bug didn’t happen when getting newWidth when hovering over a new item – weird eh?).

    Changing the CSS of the following
    #example-one li { display: inline; }

    into

    #example-one li { display: inline-block; }

    solves the problem though. It looks like it solves the Opera issue you mention in your tutorial as well!

    Keep up the great work.

  64. Husain says:

    Thanks for the idea and tutorial! I implemented it on my blog with a slight modification. There is an overline and an underline in my theme.

This comment thread is closed. If you have important information to share, you can always contact me.