Vertical Scroll Menu with jQuery Tutorial

Written by Kevin Liew on 02 Jun 2009
202,773 Views • Tutorials


Just last week, I came accross to this website Narrow Design. His scroll menu caught a lot of my attentions, I played with it for a while. Yes, unfortunately, it's built in flash. And, Yes, we are going to implement it with jquery - javascript based scroll menu that will do the same thing. Of course, it will not be 100% the same, because some of the fancy features just not that practical to implement with javascript.


So, in this tutorial, we will learn how to create a scroll menu. We will achieve the following objectives:

  • Keep html as simple as possible, and let jQuery and CSS do the rest.
  • Scoll up and down according to mouse-Y axis
  • Use jQuery.color plugin to animate the background-color changes - download jQuery.color
    (Yes, you will need this to animate the background color, I thought it will do it by default, obviously it doesn't! )

Just before we start, let me explain 3 important jquery methods we're going to use:

1. Get mouse axis

The following code will return the X and Y Axis values for your mouse pointer.

$(document).mousemove(function(e) {
$('#mouse_axis').html("X Axis : " + e.pageX + " | Y Axis " + e.pageY);

<div id="mouse_axis"></div>
2. Get objects offset

The following code will get the offset Top and Left for an object.

$(document).ready(function() {	
$('#offset').html("Top : " + $('#sidebar').offset().top + " |Left " 
+ $('#sidebar').offset().left);

<div id="offset"></div>

<div id="Sidebar">A Empty DIV named sidebar</div>
3. Get the total of selected elements by the selector.

It will return the total of selected elements.

$('#menu li').length;

Graphical Explanation

Please refer to the following div structure:

Structure for jQuery Scroll Menu

2 main DIVs #sidebar and #menu:

#sidebar : its overflow property is set to hidden. Overflow set to hidden will truncate/hide #menu's extra length and display the #menu according to the width and height of the #sidebar.

#menu : its position property is set to relative. So that if top property set to 0, #menu will snap to the top of #sidebar. So, even with no javascript, you can test the menu with random negative numbers, for example -30px, -100px or -500px. You will able to see the menu is going up. So, jQuery's job is to generate this negative values. To dynamicly generate these values, we will use the mouse-Y, because we want to scroll it up and down. It's quite complicated to explain, but we will walk through it in javascript section.


As usual, we always keep the HTML code as simple as possible. It's good to not mix javascript with html code to increase readibility and tidiness.

The SPAN in this example can be taken out, I put it in just to immitate the menu from Narrow Design.

<div id="sidebar">
<ul id="menu">
<li><a href="#">MENU 1 <span> / 2007</span></a></li>
<li><a href="#">MENU SIZE 2 <span> / 2007</span></a></li>
<li><a href="#">MENU SIZE LONG 3 <span> / 2007</span></a></li>
<li><a href="#">MENU 4 <span> / 2007</span></a></li>
<li><a href="#">MENU SIZE 5 <span> / 2007</span></a></li>
<li><a href="#">MENU SIZE LONG 6 <span> / 2007</span></a></li>

2. CSS

I have played with this CSS for quite a while to achieve the effect I want and tested it with IE as well. I was having this IE problem, where the position:relative and overflow:hidden just won't work the way it should. Fortunately, I found the solution through this website - solution to position relative and overflow in IE. Bingo, it displays exactly the same now.

#sidebar has to set as overflow:hidden to make sure the extra length in the menu is hidden. And the rest is just basic styling for the menu.

body {
margin:0 20px;

#sidebar {

#menu {

#menu li {
padding:10px 0;

#menu li a {
background:url() repeat #1f1f1f;
font-family:helvetica, arial, verdana;
padding:20px 8px 5px 20px;

#menu li span {
font-family:georgia, arial;

3. Javascript

In javascript section, I have separated all the configurable variables on the top of the script. It'll be easier to convert it to a plugin.

The most important part of this script is the last section - generate the top value based on the mouse Y value to allow user to scroll through the entire menu, and won't be affected by the offset of the sidebar as well. The mathematic equation I'm using, it's not the perfect one, but it works. If you have better suggestions, please drop me a message. : )

$(document).ready(function() {	

	//Background color, mouseover and mouseout
	var colorOver = '#31b8da';
	var colorOut = '#1f1f1f';

	//Padding, mouseover
	var padLeft = '20px';
	var padRight = '20px'
	//Default Padding
	var defpadLeft = $('#menu li a').css('paddingLeft');
	var defpadRight = $('#menu li a').css('paddingRight');
	//Animate the LI on mouse over, mouse out
	$('#menu li').click(function () {	
		//Make LI clickable
		window.location = $(this).find('a').attr('href');
	}).mouseover(function (){
		//mouse over LI and look for A element for transition
		.animate( { paddingLeft: padLeft, paddingRight: padRight}, { queue:false, duration:100 } )
		.animate( { backgroundColor: colorOver }, { queue:false, duration:200 });

	}).mouseout(function () {
		//mouse oout LI and look for A element and discard the mouse over transition
		.animate( { paddingLeft: defpadLeft, paddingRight: defpadRight}, { queue:false, duration:100 } )
		.animate( { backgroundColor: colorOut }, { queue:false, duration:200 });
	//Scroll the menu on mouse move above the #sidebar layer
	$('#sidebar').mousemove(function(e) {

		//Sidebar Offset, Top value
		var s_top = parseInt($('#sidebar').offset().top);		
		//Sidebar Offset, Bottom value
		var s_bottom = parseInt($('#sidebar').height() + s_top);
		//Roughly calculate the height of the menu by multiply height of a single LI with the total of LIs
		var mheight = parseInt($('#menu li').height() * $('#menu li').length);
		//I used this coordinate and offset values for debuggin
		$('#debugging_mouse_axis').html("X Axis : " + e.pageX + " | Y Axis " + e.pageY);
		$('#debugging_status').html(Math.round(((s_top - e.pageY)/100) * mheight / 2));
		//Calculate the top value
		//This equation is not the perfect, but it 's very close	
		var top_value = Math.round(( (s_top - e.pageY) /100) * mheight / 2)
		//Animate the #menu by chaging the top value
		$('#menu').animate({top: top_value}, { queue:false, duration:500});



That's it. Make sure you check out the demo and download the source code to play with it. Last but not least, I need your support :) If you like this article, please help me to promote it by adding this post into your bookmark. Or you can subscribe to my RSS for more posts. Thanks!

Demo Download
Join the discussion

Comments will be moderated and rel="nofollow" will be added to all links. You can wrap your coding with [code][/code] to make use of built-in syntax highlighter.

Mat 11 years ago
I made an Indexhibit theme (free) with this tutorial.
Thank you, really. A lot of people on the forum like it (just passed the 500 downloads).
Thank ypou Queness !
Izrada Web Stranica 11 years ago
Very Nice! THX from Sarajevo!
Tilnox 11 years ago
last comment was more then a year ago.. so i hope theres someone that can help me. the menu is fantastic and im using it with frames:
menu and content.

the links and the frames work fine, but theres something on the code making the link open in the same frame, even when the <base target> its pointed to the "content" and when that didnt work i pointed every link target to "content".. now the link opens in self and content.. its a silly error..

every frame has name and id (before you ask)
can someone help me?
düzce haber 11 years ago
Very Nice! THX from Sarajevo!
webdesign berlin 11 years ago
yeahh nice tutorial, merci. i think in one of the next project, i must use this script. greetz from berlin
Bulgaristanda Eğitim 11 years ago
thnx for sharing great article
altin fiyatları 11 years ago
thank you allot for sharing this useful post!
meow 11 years ago
hi, i have been playing with this scroll menu and i realised that it doesn't work up to my expectations as some 'li' s cant be seen when i resize the height of #sidebar.
when looking at the code i realised that $('#menu li').height() returns a value of 10, however as specified in the css, the li also has a padding-top of 20. thus i made such modifications to the code:
[Lines 44-45]:
//Roughly calculate the height of the menu by multiply height of a single LI with the total of LIs
var mheight = parseInt(($('#menu li').height()+20) * $('#menu li').length-1);
[Lines 52-53]:
//Explanation: ([negative vertical value of mouse position in the sidebar]/[height of sidebar])*[height of all the LIs]
var top_value = Math.round(( (s_top - e.pageY) / $('#sidebar').height()) * mheight);

took me while to figure this out and it worked for me for no matter what size the sidebar is (:
hope the rest found it useful XD
srikant 11 years ago
is there any way to extend this menu to have different levels of submenu with similar scrolling option?
Kevin Liew Admin 11 years ago
Sorry, you can't do that with this tutorial.
NB 11 years ago
The script is lagging in the current Firefox 3.6.13. No problems with IE......
Any solutions how to fix the problem?
Kevin Liew Admin 11 years ago
Yea, firefox doesnt render javascript as good as IE and chrome... unfortunately, there is no fix for this. :/
NB 11 years ago
Hey Kevin,

thanks for the fast response.

But if I open your Demo Page ( there is no lagging problem in the current Firefox Version (3.6.13)... So I'm not sure if it is a render problem..
Dennis 11 years ago

This is a great navigation! Love it! But how can I use more than one on a page? I would like to have three of these scrolls in one html page.

Kevin Liew Admin 11 years ago
hi dennis, when i was building it, multiple instances capability isn't on my mind. However, it's possible, but you will have to duplicate the whole thing, and change all the id.
Dennis 11 years ago
Cool! Thanks for the info!
Paul 11 years ago
Thanks for the fantastic work here... I've tried several different options, but can't seem to get a visited state to work. Is there a way to add a bg color change for visited links?

Kevin Liew Admin 11 years ago
I dont think you can apply visited state on it because everytime you mouse over it, it changes the color.
Miles Francis 11 years ago
can you script a code that makes the menu go down again automatically when u stop hovering the links