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.
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:
Let’s first style the “nav” ul. Open your style.css file, and add:
#nav {
position: relative;
background: #292929;
float: left;
}
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.
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.
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.
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.










Great lesson @Jeff.
Sometimes I want to convert it to the php.
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.
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
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!
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’);
});
}
“
@Kym,
What didn’t work, the jQuery functionality, or the blob?
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?
ok, I fixed it using (*) and (!important) in background property.
If you will use without vendor prefix, it will works with Opera…
Sorry my poor English…
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
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
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
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