How to Build a Lava-Lamp Style Navigation Menu

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.

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.

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.

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.

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.

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.

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.