Vertical Scroll Menu with jQuery Tutorial

Written by Kevin Liew on 02 Jun 2009
202,771 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.

Samora 11 years ago
This tut is really useful... thanks alot!!

How easy is it to replicate this horizontally?? As I would love to use this technique for a timeline??
Kevin Liew Admin 11 years ago
It will be really hard! sorry.
AGU 11 years ago
how can i replece the text for graphics buttons?! thanks
Kevin Liew Admin 11 years ago
i think you will have to style all the anchor tag to display block, set the width and height and then put the image as background image.
Edmond Mak 11 years ago
how can i change the script to make a click to the link and then show the content on the right hand within the grey box?
Harsha 11 years ago
Nice work. My requirement is not scrolling automatically on mouse move/hover.. but on scroll/dragging of a scroll bar.. similar to a listbox. The menu will have to be created on the fly from a HTTP response [JSON data]

Do you have any suggestions or hint for me?

thanks again
Kevin Liew Admin 11 years ago
gonna be a bit tricky... you should able to do it by replacing all the menu items. The script should be able to pick it up and run it normally.
emile 10 years ago
Hey I'm having a conflict with the CSS "float: left" property assigned to each <a> tag. Basically when I take "float:left" off, then it works but it takes away the styles I've applied to the each <a> tag. However when I add "float:left", everything looks fine as I want it to be but it doesn't work
Anna 10 years ago
Hi, thanks for the great tutorial, is there any way to stop the scroling ones you click on a link?
Miles 10 years ago
There is no spacing between the links in the Safari browser. How can i fix this please?
Luka 10 years ago
I'm experiencing this same bug in Chrome, Internet Explorer, and Opera on the test page. Works fine in firefox though...
Kevin Liew Admin 10 years ago
sorry, this tutorial is more about experiment than practical use in web development. But some of the reader managed to make it work though.
Luka 10 years ago
I actually fixed this bug with some tweaking to the css. Under #menu li a{, I've added float: right and 5px margin on the bottom. This keeps everything universal across all browsers. However, I noticed that in the margin space between objects, other menu tabs seem to select for no reason. Its barely noticeable, but I would recommend putting a border around these objects that is the color of your background if you want to avoid it.
Zattack 10 years ago
Is there any integration for iphones / ipads / mobile devices?
Luka 10 years ago
Any touch screen devices are going to have a seriously hard time with this, as it is based entirely on scrolling. You might want to try creating this same menu without the automatic scrolling on a redirect page for the iphone/ipad and similar touchscreen devices.
Kevin Liew Admin 10 years ago
Na, this is an old experiement. I don't think it will work with mobile device.
Samad 10 years ago
Thank you for this tutorial. Is it possible to integrate it inside a jquery lightbox?
Kevin Liew Admin 10 years ago
You need to remove the click event and integrate your lightbox.

remove this line in the js code:
window.location = $(this).find('a').attr('href');
edis 10 years ago
First of all, i have to say, i found this menu by accident a few months back, and i really liked it. I was really dissappointed that i couldn't find a way to actually use it, because it's unusable once you press ctrl and + or - which is why i had to give it up until i could find a solution for it. Also, sometimes it's not easy to stop at the menu item that you, as a user, want to click.
Today i found the flash counterpart, that doesn't have all these problems, and i hope somebody outthere has an idea how to fix the jquery version to be as usable as this flash counterpart:

If anyone has an idea, i hope to find it here on this site :)
To the coder : great try with the menu though, flash is not easy to replicate with jquery, i know :)
Keep it up!
Kevin Liew Admin 10 years ago
It was an experimental jQuery tutorial tried to imitate narrowdesign. Because it's built with html and css, all its content will get affected by the ctrl + and -, and there isn't a way to get around it.
edis 10 years ago
It is a good imitation though, i admit that . . . too bad :(
Nesta 10 years ago
Great work bro. Keep up the great work.
Animesh 10 years ago
menu is superb thanks.....a lot..
goodbyelenin 10 years ago
Thanks a lot!

If you want the links open in new window, add the following lines:

<script type="text/javascript">
$(function() {
$('a[class="tblank"]').click( function() { $(this).attr('href') );
return false;

And give all the links the "tblank" class, for example:

<a class="tblank" href="">Google <span> /01.</span></a>