How to Build a Lava-Lamp Style Navigation Menu
videos

How to Build a Lava-Lamp Style Navigation Menu

Tutorial Details
  • Technology: HTML, CSS, JavaScript
  • Difficulty: Intermediate
  • Video Length: 35 Minutes

A couple weeks ago, I created a screencast that demonstrated how to build a three-level navigation menu. In a response email, one of our readers requested a tutorial on how to build a lava-lamp style menu. Luckily, it’s quite a simple task, especially when using a JavaScript library. We’ll build one from scratch today.

Screenshot

Prefer a Screencast?

Step 1 Create the Mark-up

Before we can create this neat functionality, we need a base from which to work from. In your favorite code editor, create an unordered list for your navigation, and import both jQuery and jQuery UI, via Google.

<!DOCTYPE html>

<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
	<title>SpasticNav  Plugin</title>
	<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />
</head>
<body>

<div id="container">

	<ul id="nav">
		<li id="selected"><a href="#">Home</a></li>
		<li><a href="#">About</a></li>
		<li><a href="#">Blog</a></li>
		<li><a href="#">More About My Portfolio</a></li>
		<li><a href="#">Contact</a></li>
	</ul>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js" type="text/javascript"></script>	

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
     
</body>
</html>	

Note how we gave an id of “selected” to the home page. This is fairly standard in most websites; it allows use to target the current page, and style that particular list item accordingly.

Next, we must decide how to best implement the lava-lamp functionality. To allow for reusability, we’ll package this little script into a plugin, and call it like:

$('#nav').spasticNav();

Since we’ve decided to build a plugin, let’s go ahead and create a new file for that script, and reference it in our mark-up. We’ll call it jquery.spasticNav.js.

<script type="text/javascript" src="js/jquery.spasticNav.js"></script>	

<script type="text/javascript">
$('#nav').spasticNav();
</script>
</body>

Step 2 Beginning the Plugin

To reduce the number of global variables that we must create, as well as remove any possibilities of the $ symbol clashing with other JavaScript libraries, let’s wrap our plugin in a self-executing anonymous function.

(function($) {
   
})(jQuery);

Now, jQuery will be passed into our plugin, and will be represented via the $ symbol.

Next, it’s generally a best practice to give the users of the plugin as much flexibility as possible. As such, we’ll give them the option of passing in an object-literal when they call the plugin to override a handful of settings. As I see it, they should be able to:

  • Set the amount of overlap for our little blob. This refers to how much the blob will exceed the height of the navigation menu.
  • Set the speed
  • Set a reset, which causes the blob to move back to the current page item (assuming that the user never clicks on a link)
  • Set the color of the blob. This can be accomplished with CSS, but it’s a nice convenience, nonetheless.
  • Set the easing option.

Now, we’ll name our plugin, and make it equal to a function. $.fn is simply an alias for jquery.prototype.

$.fn.spasticNav = function(options) {

};

Knowing that we’ll be allowing these overrides, we must make sure that we accept an “options” parameter.

Step 3 Configuration Options

Now that we’ve named our plugin, the next step is to create the configuration options.

options = $.extend({
	overlap : 20,
	speed : 500,
	reset : 1500,
	color : '#0b2b61',
	easing : 'easeOutExpo'
}, options);    

Above, we’re taking the options variable, setting some default properties and values, and then extending it with whatever (if anything) the user passes in when they call the plugin. That way, the options they pass will override our default settings. For example, if, when I call this plugin, I pass:

$('#nav').spasticNav({
   speed : 2000,
   easing : 'easeOutElastic'  
});

Those two properties will override the default settings, while the remainder of the options will remain the same.

Step 4 Implementing the Functionality

Now, we’re ready to cycle through each element that was passed to this plugin, and implement the lava-lamp functionality. Remember, we can’t assume that the user is going to pass a single element to this plugin. They could, if they wanted, reference a class, which refers to multiple items that should receive this functionality. As such, we’ll call this.each to iterate over each item in the wrapped set.

return this.each(function() {

});

Within this function, we’ll create some variables. Not all of them will immediately have values, but since the JavaScript engine will hoist all variable names to the top of the function anyways (behind the scenes), it’s generally a best practice to declare them at the top, and then initialize them later.

var nav = $(this),
	currentPageItem = $('#selected', nav),
	blob,
	reset;
  • nav : “Caches” this, wrapped in the jQuery object.
  • currentPageItem : Contains the list item with an id of selected. We pass a second parameter to set the context to search from. That way, we don’t have to traverse the entire dom to find this element.
  • blob : For lack of a better word, this variable will reference the highlighter, that will follow our mouse when we hover over the menu.
  • reset : This will store a reference to the setTimeout function that will create later. It’s needed in order to call clearTimeout. More on this soon…

Now that we’ve declared/initialized our variables, let’s create the actual blob, so to speak.

$('<li id="blob"></li>').css({
	width : currentPageItem.outerWidth(),
	height : currentPageItem.outerHeight() + options.overlap,
	left : currentPageItem.position().left,
	top : currentPageItem.position().top - options.overlap / 2,
	backgroundColor : options.color
}).appendTo(this);

The reason why we’re calling the CSS method, rather than simply adding a class, is because these values will vary depending on the current page’s list item. As such, we must use JavaScript to retrieve they values.

  • width: Get the width of currentPageItem, including any borders and padding.
  • height: Get the height of currentPageItem, including any borders and padding. Also, add the amount of overlap, to make the blob extend outside of the menu.
  • left: Sets the left property of the blob equal to the left position of the currentPageItem. (We must set a positioning context in our CSS for this value to take effect.)
  • top: Sets the top value as well, and vertically centers the blob.
  • backgroundColor: Sets the background color.

Finally, we append this new list item to this, or #nav.

Next, we need to store a reference to #blob. That way, we don’t have to search the DOM everytime we wish to access it. We declared the blob variable at the top of the function. Now, let’s initialize it.

blob = $('#blob', nav);

Step 5 The Hover Event

We must now “listen” for when the user hovers over one of the list items (excluding the blob of course) in our navigation menu. When they do, we’ll set the width and left properties of the blob equal to that of the currently hovered list item.

$('li:not(#blob)', nav).hover(function() {
	// mouse over
	clearTimeout(reset);
	blob.animate(
		{
			left : $(this).position().left,
			width : $(this).width()
		},
		{
			duration : options.speed,
			easing : options.easing,
			queue : false
		}
	);
}, function() {
	// mouse out	
	reset = setTimeout(function() {
		blob.animate({
			width : currentPageItem.outerWidth(),
			left : currentPageItem.position().left
		}, options.speed)
	}, options.reset);
	
});

To summarize the script above…

  • Get all list items – not the #blob – within the navigation menu, and when they’re hovered over, run a function.
  • Animate the blob, and set its left and width values equal to that of the hovered list item.
  • Pass an object literal as the second parameter of animate, and set the duration and easing equal to what we set in our configuration options. Set queue to false to prevent animation build-up.
  • When they mouse out, call setTimeOut, which will push the blob back to the current page item. If we didn’t do this, and the user didn’t click on a navigation link, the menu would show that they were on
    a different page entirely. This will, after a second or so, animate the blob back to currentPageItem.

And that’s all there is to it! This is a super simple plugin. The next step is to style our navigation menu.

Step 6 Styling the Menu

Without any styling, our menu should look similar to this:

Unstyled mark-up

Let’s first style the “nav” ul. Open your style.css file, and add:

#nav {
	position: relative;
	background: #292929;
	float: left;
}    
Styling the navigation menu

Next, we’ll style each list item.

#nav li {
	float: left;
	list-style: none;
	border-right: 1px solid #4a4a4a;
	border-left: 1px solid black;
}    

This simply floats each list item to the left, and adds a border to each side.

Styling the list items

Moving along, we next must style the anchor tags within our navigation menu.

#nav li a {
	color: #e3e3e3;
	position: relative;
	z-index: 2;
	float: left;
	font-size: 30px;
	font-family: helvetica, arial, sans-serif;
	text-decoration: none;
	padding: 30px 45px;
}    

We’re setting a color, floating them to the left, setting some font values, and a healthy amount of padding. Take note of the z-index property. This is a necessity, and will be explained shortly. However, remember that, in order to adjust the z-index, we must set a positioning context, which we’ve done.

Styling the anchor tags

Because we’re not implementing a full reset stylesheet, let’s ensure that we zero out any default margins and padding on our ul and li, just to save any potential headaches.

ul, li {
	margin: 0; padding: 0;
}    

The last step is to style the blob itself!

#blob {
	border-right: 1px solid #0059ec;
	border-left: 1px solid #0059ec;
	position: absolute;
	top: 0;
	z-index : 1;
	background: #0b2b61;
	background: -moz-linear-gradient(top, #0b2b61, #1153c0);
	background: -webkit-gradient(linear, left top, left bottom, from(#0b2b61), to(#1153c0));
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
	-moz-box-shadow: 2px 3px 10px #011331;
	-webkit-box-shadow: 2px 3px 10px #011331;
}    

Once again, we set some pretty colors for our borders, and add some background colors (including CSS3 gradients/borders/shadows for Firefox and Safari/Chrome). Once again, we see that z-index property. Without this, the blob will display above all of the text in the navigation menu. To counter this, we must be sure that its z-index property is LOWER than the list item’s! We also must set the position to absolute in order to adjust its top and left values with our plugin.

Screenshot

Conclusion

That’s all there is to it! With minimal effort, we’ve created a really neat looking navigation menu from scratch. Let me know if you have any questions! Thanks for reading and watching.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.indonesianforum.org perritos

    Great lesson @Jeff.
    Sometimes I want to convert it to the php.

  • the pepino

    Yes indeed very good,

    but I will not post a code that i don’t recoinage,

    Like the one you do a reference on,

    Before you explain it to me read it and organize it with comments…

    tanks,
    thepepino.

  • Kym

    When I tried this, it works fine on macs, and in mozilla firefox, but it would not work in internet explorer and i could not figure out why.

    Kym

    • http://www.neilrpearce.co.uk neil

      Yep! Me to. The demo works fine in IE8 but not when i do it. I have completely copied all the source code, viewed it in FF – works fine……but it will not work in IE!!!!

      Is there some secret code….? Got to be something as i have completely used all the source code!

    • http://www.epicbaydesigns.com Wendi

      Hi

      I had the same issue on IE8 even with the demo. The quick and dirty solution I came up with was to detect IE browsers and then add the rounded corners using DD_Roundies (http://www.dillerdesign.com/experiment/DD_roundies/). I used the soon to be deprecated Jquery $browser to detect IE . Here is the code I added to the plugin ~line 26 (after $(”).css({…append(this)). Note I’m using quotes in case of stripping by the editor:

      if($.browser.msie){
      $.getScript(‘scripts/DD_roundies_0.0.2a-min.js’,function(){
      DD_roundies.addRule(‘#blob’, ’4px’);

      });

      }

      • K

        Well, it worked for me all right. I use IE9 and Chrome, worked well with both of them.

  • http://www.ecustom.ca/ Damon Bridges

    @Kym,

    What didn’t work, the jQuery functionality, or the blob?

  • willi

    Hi, this is a great navigation menu.
    I modified the blob color in the css file and it looks great in chrome and ff, but in IE7 it always has de same blue color. Can you help me with that?

    • willi

      ok, I fixed it using (*) and (!important) in background property.

  • http://protonw.blogspot.com/ metude

    If you will use without vendor prefix, it will works with Opera…

    Sorry my poor English…

  • nosize

    Hi Jeff, and evryone.
    I have been struggling with this menu for a week, wanted to solve it on my own but now i guess i am
    I keep getting this error from firebug:

    “$(“#nav”).spasticNav is not a function
    $(‘#nav’).spasticNav();”
    please help me out

  • Kahil

    I keep getting this error. I have this script and everything loading and working on another site just fine. All I did was copy and paste it to a new site and it won’t work. I always get this error. Any ideas????

    currentPageItem.position() is null
    http://mjcengineering.com/mjc/js/jquery.spasticNav.js
    Line 23

  • Amo

    Hello, and thanks for the excellent tutorial. i have got it working fine for my needs,except i need to centre the whole thing in the middle of the page. it is currently sitting slightly to the left. can any one help me with this please..thanks

  • Mahesh

    Hi,

    I was able to get this menu to work on my webpage. However, the menu is not ajax based. So somehow the selection does not stick when I click and go to a new page. I saw a few people complaining about the same. How to get the Javascript look at current page and select the menu accordingly?

    THanks

  • Chris

    Hey Jeffrey and everyone else,

    first of all, nice tutorial and nice effect.

    Well I’m pretty new to jQuery and programming at all. So don’t be harsh on me if I’ve misses something. I’ve implemented your navigation and everything is working well so far, but I recognized that the readout of the currentPageItem isn’t working correctly.
    Instead of a color I used a graphic for #blob which should align on top and center.
    So because of the incorrect readout the selected item is not aligned the right way.

    Example:

    http://www.webdesign-radebeul.de/3

  • http://www.leafkit.com Rob

    Hey, I’m new to Javascript & AJAX, and I was trying to get lightbox working at the same time as this script, what would be the problem I might be running into?

  • George

    Helo Sir thanks for the great tutorial, i had a question and hopefully you have the answer, in this menu when i mouseover to other menu list this blob is moving to other list where i move my mouse (as it should), so how can i make it work that when it is on other menu it will display those menu text in other color and when id=selected it will have other color.

  • Tara

    Thanks so much for this. The lavalamp navigation is one of my favs =]

  • Cesar

    Great tutorial. I even figured out how to make it stick to the current page and not slide back to “home”. I did run across a problem though, my blob overlaps my navigation, thus it’s not clickable. Any ideas on a solution, help?

    • noname

      Could you plz post here how did you managed to to make it stick to the current page and not slide back to “home” ?

      • http://www.nicwithaholeinit.com Nichole

        Yes, please post how you made the blob stick to the current page and not slide back to HOME. This is a pretty cool navigation, with that exception. Thanks so very much for your time.

    • http://www.nicwithaholeinit.com Nichole

      To keep the blob on the current page in the nav, I think I’ve found the area(s) in the code that control this. What needs to be inserted to replace the position name to keep the blob on the current page? As it sits now the blob always returns left to the HOME button. Please post how you figured out how to keep the blob on the current page?

      left : currentPageItem.position().left,

      Thanks bunches for your help!

      • MrsD

        Try doing something like this to you nav li…

        Then add in your CSS file the appropriate styles (ie. if you want text to be a different color)

      • Jay

        Just change your to

  • http://www.raregal.com dinesh

    this is woorking. But plz tell me how to give link for this. The link is not working

  • http://www.umbraprojekt.pl/ mingos

    Very nice. I have to admit I didn’t quite understand the jQuery part, but that’s because I started learning it only a few days ago. Still, I managed to put this nav (somewhat modified to fit Drupal’s menu system) in a production site: http://www.bniolsztyn.pl/ (in Polish, sorry – but it’s about how Jeffrey’s nav looks, not the content :D).

  • http://www.naymz.com/kamrul_hasan_1607777 Kamrul (Affordable SEO)

    That is a little advanced and not ie 6 compatible. Putting this for future. May be after 2014 when there will be no xp support.

    • http://www.naymz.com/kamrul_hasan_1607777 Kamrul (Affordable SEO)

      Or, perhaps just a little css modification required?

  • http://www.naymz.com/kamrul_hasan_1607777 Kamrul (Affordable SEO)

    NOTE for Comments Moderator: – Please remove my previous comments and put this comment.

    Comment:

    “Thank you for your nice plug in. I am trying to use it in my joomla! site. But the problem is, this plug-in always requires an active menu item. Whenever I am in a page which is not in the joomla ‘top menu’ this does not work.

    So, could you please tell me what to do?”

  • Ryan

    Great tutorial. What editor are you using? I love those shortcuts you use to created div id’s and lists… Dreamweaver doesn’t seem to do this.

    Ryan.

  • http://www.markdunbavan.co.uk Mark D

    Hi.

    I have adapted this lava lamp plugin to my site I am doing at the moment.

    The problem I have is when I click the nav link, the box juts to the left progressively on each nav. So for instance, home is okay, but then about juts to the left about 2px then services juts out at about 4px, then contact juts out at 6px… and so on.

    Does anybody know what may be causing this to happen with it?

    Thanks
    Mark

  • http://www.degisimbilisim.com balıkesir web

    good Great tutorial. very thanks this sharing…

  • http://funstoo.blogspot.com Ikhsan Funstoo

    Excellent Tutorial! Love it Just bookmarked this web!
    thank you very much!

  • http://www.asktechtz.com Aaron

    Great tutorial,very very useful.Thanks for sharing,

  • Kakrona

    Hi, man! This is really cool menu. i want to put it in my assignment. But I got some problems I can’t figure out. The problem is I want the left-bottom of the background to be curved 50px and also the blob, I mean when the blog move the the left-side, I want the left-bottom of the blob curved 50px too… :(

    Anybody has an idea… Pls help me…

    Millions thanks…..

    This is my e-mail: kakrona.chan@gmail.com – I’m waiting for u guys…

  • http://www.facemetin.pl/ huehr

    great, is work !!

  • http://darnell964dougl.livejournal.com/data/rss Hobo Bags

    hey, this is really cool , Im happy I found this article cuz i been surfing for something like this since last night. good looking. Would you mind if I send a link to my email list?

  • Propho

    Could someone take a look at http://tansyn.com/ariake I got the menu working fine in Firefox but it is totally screwed up in Internet Explorer.. The menu shows as a list and does not properly display across the top like it should..

    Thanks in Advance..

  • http://tecnowii.altervista.org Clear

    Hi, i’v got two problems with your menu… Could you help me please?

    1st) i can not edit the height of the blob, i tried with:

    height : currentPageItem.outerHeight(47px) + options.overlap,

    but nothing… How do i?

    2nd) How put two different color of link? if the link is in the blob i want white color if the link is out of the blob i want gray color,but the link color is only one of #nav li a … :S how do i? xD

  • http://www.brettwidmann.com Brett Widmann

    This was a very helpful tutorial! Everything turned out great.

  • http://www.nicwithaholeinit.com Nichole

    To keep the blob on the current page in the nav, I think I’ve found the area(s) in the code that control this. What needs to be inserted to replace the position name to keep the blob on the current page? As it sits now the blob always returns left to the HOME button.

    left : currentPageItem.position().left,

    Thanks bunches for your help!

    • http://www.nicwithaholeinit.com Nichole

      never mind…got it all good

      • Adrian

        @Nichole, can you share what you did to achieve this?
        I haven’t tried this tut yet but am planning on trying it out on a new site

      • Adrian

        actually never mind (for me at least)
        I like the fact it resets to the home link (or whatever the current page is)
        I guess if you don’t want it to though, you can always remove the reset option from the top of the script

  • http://ideagrafika.pl IdeaGrafika

    Clever Idea :) Good to practice

  • Tim

    Hello first of all great tut!

    But I got a problem :s, if I try to use this in a mvc-framework, it only works on one URL (http://localhost/CI/).
    When ever I click a link that refers me to another page (http://localhost/CI/index.php/home/index), my css will still load but the blue bubble won’t.

    I tried it in a normal html withouth the framework and it works fine there.

    Could someone help me?
    Thx.

  • Tim

    nvm fixed it :)

    • Ryan

      Tim, how did you fix it? Please share

  • Rylie

    Dear Jeffrey,

    I’m having a little trouble wrapping my head around the $ being an alias for the jQuery function. Can you explain in as much detail as you can how jQuery achieves this aliasing- how does it define the $ to be an alias for the jQuery function. I don’t mean show me the code that does this- I mean, please explain the code that does this aliasing, i.e. how it works.

    Thanks so much for teaching us!

  • Alex Bars

    Thanks, Jeffrey!

  • CJ

    Not work on internet explorer. Is one of best script that i see ever, but if dont work with internet explorer i not good idea use it because there is many user with that explorer. I hope you can fix that.

  • sera

    i cannot do ! please teach me ~~

  • Marko Rosic

    I’m trying to implement this menu on Drupal 7 with slight modifications to design… blob shows where it should but it doesn’t move.

    (function ($) {

    Drupal.behaviors.spasticNav = {
    attach: function(context, settings) {

    // Lava Lamp Meny Effect

    $.fn.spasticNav = function(options) {

    options = $.extend({
    overlap : 0,
    speed : 500,
    reset : 1500,
    background : ‘../images/menu-active-bg.png’,
    easing : ‘easeOutExpo’
    }, options);

    return this.each(function() {

    var nav = $(this),
    currentPageItem = $(‘li.active’, nav),
    blob,
    reset;

    $(‘<li id=”blob”></li>’).css({
    width : currentPageItem.outerWidth(),
    height : currentPageItem.outerHeight() + options.overlap,
    left : currentPageItem.position().left,
    top : currentPageItem.position().top – options.overlap / 2,
    backgroundImage : options.background
    }).appendTo(this);

  • http://www.rouse.ws William Rouse

    Jeffery:
    I wish you would return this tutorial and show how to get a multi-level of some kind.
    Thanks!

  • http://www.electricmayhemsolutions.net Adrian

    Has anybody managed to get this working in WordPress? I have jQuery working in general, but I have been playing around with this menu for a while and cannot seem to even get the “blob” ID to become associated with the “nav”

    I’d really like my blog page to look the same as the rest of my site!

    • http://www.electricmayhemsolutions.net Adrian

      I now have this working, but just in a “non-WordPress recommended kind of way”

      Now that I at least have a fallback position, I’ll step back through the “WordPress right way” of implementing this and see if I can improve on what I have currently. But at least my blog page looks the same as the rest of my site

  • http://met2tr.com metin2 pvp

    Thanks, Jeffrey!

  • MO

    Very Nice!!!

    How do you link to an iframe? So that when a menu item is clicked, it opens in an iframe on the main page.

  • http://puluthriau.blogspot.com puluth®

    Wow, thanks, i like it. . .

  • http://www.panjibali.com Panji

    its posible use background like narow at the bottom ( like a tool tip)

  • http://www.sweetmarketing.pl Pozycjonowanie

    I had some problems with Opera but finally got it working.

  • jay

    The blob should return to the current item on mouseOut. Also, the button gets an outline around it when clicked, you should set CSS
    :focus {outline: 0;}

  • http://www.pxlefx.com Ashok

    The script is not working…

    I have checked in all browsers (ie7, FF4 & Chrome11)

    Please check it and fix the problem as soon as possible…

  • anie

    i want to change the height of blob.. how? any one help me please

  • http://www.reklamamobilna.pl reklama mobilna

    Looks to be just what I need. Going to check it out. Thanks.