Vertical Scroll Menu with jQuery Tutorial

Vertical Scroll Menu with jQuery Tutorial

Vertical Scroll Menu with jQuery Tutorial


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!

Author: Kevin Liew

Kevin Liew is a web designer and developer and keen on contributing to the web development industry. He loves frontend development and absolutely amazed by jQuery. Feel free to say hi to me, or follow @quenesswebblog on twitter.

Share the love


Wed, 7th July 2010
Great tut. I am struggling to separate the list items in my wordpress sidbar. I have managed to get the plugin into the page and working but it treats each section ie. recent posts as one list item and highlights 4 or 5 items as one. Am probably doing something stupid but fairly new to wordpress and PHP. Reply
Mon, 19th July 2010
hey thanks dude..been studying CSS and HTML these days Reply
Atilla Denis
Wed, 21st July 2010
Hi, Really like the menu but I seem to be having some problems with the href links when I add a target value to them to target an iframe. Just get a new page instead of it opening in iframe of choice. Any ideas? Reply
Wed, 4th August 2010
This is very nice i use on my site.
Thank you so much for the inspiration and code. Reply
Sat, 14th August 2010
Hi there,
Great looking menu. I seem to be having a problem getting the thing to animate at all. Java script is enabled. Has anyone else had this problem? If so any ideas on what might be happening? Thanks so much! Reply
Mon, 23rd August 2010
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 ! Reply
Izrada Web Stranica
Tue, 31st August 2010
Very Nice! THX from Sarajevo! Reply
Wed, 29th September 2010
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? Reply
düzce haber
Sat, 9th October 2010
Very Nice! THX from Sarajevo! Reply
webdesign berlin
Sat, 23rd October 2010
yeahh nice tutorial, merci. i think in one of the next project, i must use this script. greetz from berlin Reply
Bulgaristanda Eğitim
Sat, 6th November 2010
thnx for sharing great article Reply
altin fiyatları
Wed, 17th November 2010
thank you allot for sharing this useful post! Reply
Sun, 2nd January 2011
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 Reply
Wed, 12th January 2011
is there any way to extend this menu to have different levels of submenu with similar scrolling option? Reply
Kevin Liew Admin
Sun, 16th January 2011
Sorry, you can't do that with this tutorial. Reply
Mon, 24th January 2011
The script is lagging in the current Firefox 3.6.13. No problems with IE......
Any solutions how to fix the problem? Reply
Kevin Liew Admin
Tue, 25th January 2011
Yea, firefox doesnt render javascript as good as IE and chrome... unfortunately, there is no fix for this. :/ Reply
Wed, 26th January 2011
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.. Reply
Wed, 16th February 2011

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
Wed, 16th February 2011
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. Reply
Thu, 17th February 2011
Cool! Thanks for the info! Reply
Wed, 23rd February 2011
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?

Thanks Reply
Kevin Liew Admin
Sat, 26th February 2011
I dont think you can apply visited state on it because everytime you mouse over it, it changes the color. Reply
Miles Francis
Wed, 2nd March 2011
can you script a code that makes the menu go down again automatically when u stop hovering the links Reply
Fri, 4th March 2011
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?? Reply
Kevin Liew Admin
Sun, 6th March 2011
It will be really hard! sorry. Reply
Mon, 7th March 2011
how can i replece the text for graphics buttons?! thanks Reply
Kevin Liew Admin
Wed, 9th March 2011
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. Reply
Edmond Mak
Tue, 8th March 2011
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? Reply
Thu, 10th March 2011
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
Thu, 10th March 2011
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. Reply
Wed, 6th April 2011
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 Reply
Wed, 6th April 2011
Hi, thanks for the great tutorial, is there any way to stop the scroling ones you click on a link? Reply
Thu, 28th April 2011
There is no spacing between the links in the Safari browser. How can i fix this please?
Thx Reply
Fri, 13th May 2011
I'm experiencing this same bug in Chrome, Internet Explorer, and Opera on the test page. Works fine in firefox though... Reply
Kevin Liew Admin
Sun, 15th May 2011
sorry, this tutorial is more about experiment than practical use in web development. But some of the reader managed to make it work though. Reply
Tue, 17th May 2011
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. Reply
Fri, 29th April 2011
Is there any integration for iphones / ipads / mobile devices? Reply
Fri, 13th May 2011
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. Reply
Kevin Liew Admin
Sun, 15th May 2011
Na, this is an old experiement. I don't think it will work with mobile device. Reply
Tue, 24th May 2011
Thank you for this tutorial. Is it possible to integrate it inside a jquery lightbox?
Regards Reply
Kevin Liew Admin
Sun, 29th May 2011
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'); Reply
Tue, 23rd August 2011
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! Reply
Kevin Liew Admin
Wed, 24th August 2011
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. Reply
Fri, 2nd September 2011
It is a good imitation though, i admit that . . . too bad :( Reply
Tue, 23rd August 2011
Great work bro. Keep up the great work.
Wed, 28th September 2011
menu is superb thanks.....a lot.. Reply
Mon, 7th November 2011
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> Reply
Tue, 15th November 2011
hey a have a problem. how i make a sub item ? Reply
Graham Morley
Sun, 12th February 2012
I took what you have done and made my own version, based on percentages. no matter how big the outside div is, the menu will move up and down according to the percentage the mouse has moved across it. This way, as you come near the top or bottom, the inside div won't keep scrolling past and leave a huge gap at the top/bottom respectively. This also makes the scrolling motion feel more natural. Give it a try if you're interested

$(document).ready(function() {

//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);
//Sidebar height
var s_height = s_bottom - s_top;
//Mouse Y axis height
var mouseHeight = (e.pageY - s_top);
//calculate percentage of total height mouse has moved
var mousePer = mouseHeight / s_height;

//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);

// the value 70 and 20 are there to make the very top and very bottom display a little nice
// in my application. If you find there's too big or too small of a gap at the top or bottom
// of your list, change the numbers around a bit. Get rid of them for a 100% mouse movement
// top to bottom
var top_value = (mousePer * (mheight - s_height + 70) * -1) + 20;

//Animate the #venues by chaging the top value
if ( (mheight - s_height) > 0 ) {
$('#menu').animate({top: top_value}, { queue:false, duration:500});

</script> Reply
Thu, 22nd March 2012
your script is very dynamic and perfect :) Reply
Mon, 30th April 2012

Graham / Amine, I'm trying to add this updated code into the downloaded source file but am having difficulty making it work again. :S I'm just learning java, please help! Why might copy/pasting this new code not work?? I'm adding/replacing everything after "//Scroll the menu on mouse move above the #sidebar layer"...
Fri, 15th June 2012
nine hundred works well on demo page
"var top_value = (mousePer * (mheight - s_height + 900) * -1) + 20; " Reply
The new guy
Thu, 7th June 2012

I'm very new in jquery. I have tried to implement this on my site for a few days and I couldn't find the way to implement the javascript. In my site I already insert the html and the css, I see the menu in my site without any animation. I tried to paste the javascript code in to the function.php in my wordpress and damage my page. I also tried to include the source code files in js file in my theme and did not work.

Can someone explain step by step on how to include the javascript in to wordpress?
In which file I have to paste the code provided in this tutorial?
If I have to include any js file by ftp?

I need help to implement this nice menu in to my wordpress site. Thanks Reply
Jack Otieno
Fri, 29th June 2012
This tutorial is quite awesome. Thanks alot Reply
Mon, 13th August 2012
for all those who are little iritated that menu is sliding much too top add a following line

$('#sidebar').mouseleave(function () {
$('#menu').animate({top: 0}, { queue:false, duration:500});
this will do that menu will reset (almost) to its start position after mouse eaves sidebar area

i was wanderring that Graham fixxed his condition for a little bit softer menu moves without bottom or top vanisshing :) Reply
Tue, 14th August 2012
can anyone help me make it so that each 'li' has a different background colour on colorOver and colorOut so each li is different
thankyouu Reply
Wed, 20th February 2013
Nice post. thanks for sharing. Reply
Mon, 20th October 2014
Thanks for scroll menu, i'm going to use it. But I have a problem, i cannot change the width of each li element, it's width depends on text's length. How can I make them with same width?

Leave a Comment

Please keep in mind that comments are moderated and rel="nofollow" is in use. You can use [code][/code] if you want to write codes. Don't spam us :) Thanks!