simple slider menu with jQuery
There are possibly a million tutorials out there about jQuery and menus, but there weren’t a million and one… until now! ;) I recently had cause to make a sliding drop-down menu for a project at work and thought I would write up how I did it. First off, check out the demo. The commented source code is below.
Markup
We have a regular unordered list with links. Sub-menus are nested unordered lists with the sub-menu
class applied. Sub-menus are optional; Link 3 for example doesn’t have one. We wrap the text of each link in a span so that we can adjust the font size without having to do any width adjustments, since the width of each link is based on the font size because we use the em
unit in the CSS. The width of each sub-menu link is the same as the parent menu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<ul> <li> <a href="#"><span>Link 1</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1<span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 2</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 3</span></a> </li> <li> <a href="#"><span>Link 4</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> <li> <a href="#"><span>Sub-link 4</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 5</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> <li> <a href="#"><span>Sub-link 4</span></a> </li> <li> <a href="#"><span>Sub-link 5</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 6</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> </ul> </li> </ul> |
Styling
We style the outer list such that links appear horizontally next to each other, and there aren’t any bullets. There’s a small space between each link horizontally. We style sub-menus so that their text is smaller, they are more compact vertically, and the colors are a bit different. We also set the z-index
property on sub-menus to be very large, so whenever a sub-menu is displayed it will be on top of other content in the page. Sub-menus are hidden by default. We position them with absolute
so that they don’t force other elements in the page to move out of the way.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
.sub-menu { display: none; position: absolute; left: 0; float: none; z-index: 9000; margin: 0 4px 0 0; } .sub-menu li { float: none; margin-bottom: 0; } .sub-menu li a { margin: 0; padding-bottom: 8px; display: block; padding: 0.5em 1.25em 0.5em 1em; background-color: #CAD6D4; font-weight: 300; color: #404443; border-left: 0.25em solid #BBCCC9; } .sub-menu li a:hover, .sub-menu li a:focus { text-decoration: none; } .sub-menu li a span { font-size: 80%; } ul { margin: 0; padding: 0; list-style: none; position: relative; } ul li { float: left; position: relative; margin-bottom: 0.5em; } ul li a { margin-right: 0.5em; width: 6em; text-decoration: none; display: block; padding: 1em 1.25em; background-color: #D6DFDD; font: 700 22px/28px "Helvetica Neue", Helvetica, sans-serif; letter-spacing: 0.2em; color: #05C7F2; border-bottom: 1px solid #eeeeee; position: relative; } ul li a:hover, ul li a:focus { background-color: #A61449; color: white; } |
Code
When the page loads, we set up some hover
handlers for the links in the menu. When a top-level link is hovered over, we display the sub-menu for that link. When focus leaves the top-level link and all of its sub-links, after a half-second delay we hide the sub-menu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
$(function() { var delayBeforeClosing = 500; // 1/2 second // Create an array to hold the timers for closing each individual sub-menu. This way the sub-menus function independently, so we can see multiple ones open at once and they close in the order they were opened. Get the number of top-level links in the menu, and use that to initialize an array of that size. var closeTimeouts = new Array($('ul > li').length); // Define a function that will be called any time a link in the menu is hovered on. Takes a link and the sub-menu associated with it. jQuery's $() returns an array, so it's possible the sub-menu we were given is an empty array, meaning the given top-level link has no sub-menu. In which case, return because we don't need to do anything else. Otherwise, see which top-level link contains the sub-menu, and reset its close timer. Then, display the sub-menu by sliding it down into view. var menuLinkOnHover = function(link, subMenu) { if (subMenu.length < 1) { return; } var mainLinkIndex = subMenu.parent().index(); clearTimeout(closeTimeouts[mainLinkIndex]); subMenu.stop(true, true).slideDown(); }; // Define a function that will be called any time you move your mouse off of a link in the menu. Takes a link and the sub-menu associated with it. Ensure there is actually a sub-menu to work with before proceeding. See which top-level link contains the sub-menu, and create a close timer for it that will execute an inline function in 1/2 second. That inline function closes the sub-menu by sliding it up out of view. The close timer might be wiped out, meaning the sub-menu will not get closed, if the user moves their mouse back onto a link associated with the sub-menu (either the top-level link or a link within the sub-menu). var menuLinkOnLeave = function(link, subMenu) { if (subMenu.length < 1) { return; } var mainLinkIndex = subMenu.parent().index(); closeTimeouts[mainLinkIndex] = setTimeout(function() { subMenu.stop(true, true).slideUp(); }, delayBeforeClosing); }; // Set up mouse hover handlers for top-level links in the menu. Call the functions defined above. The associated sub-menu for each link is the next element after the link. $('ul > li > a').hover( function() { var link = $(this); menuLinkOnHover(link, link.next('.sub-menu')); }, function() { var link = $(this); menuLinkOnLeave(link, link.next('.sub-menu')); } ); // Set up mouse hover handlers for links in the sub-menus. Call the functions defined above. The associate sub-menu for each link is the closest element with the 'sub-menu' CSS class in the DOM. $('ul ul.sub-menu a').hover( function() { var link = $(this); menuLinkOnHover(link, link.closest('.sub-menu')); }, function() { var link = $(this); menuLinkOnLeave(link, link.closest('.sub-menu')); } ); }); |
Everything
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title> Simple Slider Menu Demo </title> <meta name="viewport" content="width=device-width"> <style type="text/css"> body { padding: 4em; background-color: white; } .sub-menu { display: none; position: absolute; left: 0; float: none; z-index: 9000; margin: 0 4px 0 0; } .sub-menu li { float: none; margin-bottom: 0; } .sub-menu li a { margin: 0; padding-bottom: 8px; display: block; padding: 0.5em 1.25em 0.5em 1em; background-color: #CAD6D4; font-weight: 300; color: #404443; border-left: 0.25em solid #BBCCC9; } .sub-menu li a:hover, .sub-menu li a:focus { text-decoration: none; } .sub-menu li a span { font-size: 80%; } ul { margin: 0; padding: 0; list-style: none; position: relative; } ul li { float: left; position: relative; margin-bottom: 0.5em; } ul li a { margin-right: 0.5em; width: 6em; text-decoration: none; display: block; padding: 1em 1.25em; background-color: #D6DFDD; font: 700 22px/28px "Helvetica Neue", Helvetica, sans-serif; letter-spacing: 0.2em; color: #05C7F2; border-bottom: 1px solid #eeeeee; position: relative; } ul li a:hover, ul li a:focus { background-color: #A61449; color: white; } </style> </head> <body> <ul class="nav"> <li> <a href="#"><span>Link 1</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1<span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 2</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 3</span></a> </li> <li> <a href="#"><span>Link 4</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> <li> <a href="#"><span>Sub-link 4</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 5</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> <li> <a href="#"><span>Sub-link 3</span></a> </li> <li> <a href="#"><span>Sub-link 4</span></a> </li> <li> <a href="#"><span>Sub-link 5</span></a> </li> </ul> </li> <li> <a href="#"><span>Link 6</span></a> <ul class="sub-menu"> <li> <a href="#"><span>Sub-link 1</span></a> </li> <li> <a href="#"><span>Sub-link 2</span></a> </li> </ul> </li> </ul> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function() { var delayBeforeClosing = 500; var closeTimeouts = new Array($('ul > li').length); var menuLinkOnHover = function(link, subMenu) { if (subMenu.length < 1) { return; } var mainLinkIndex = subMenu.parent().index(); clearTimeout(closeTimeouts[mainLinkIndex]); subMenu.stop(true, true).slideDown(); }; var menuLinkOnLeave = function(link, subMenu) { if (subMenu.length < 1) { return; } var mainLinkIndex = subMenu.parent().index(); closeTimeouts[mainLinkIndex] = setTimeout(function() { subMenu.stop(true, true).slideUp(); }, delayBeforeClosing); }; $('ul > li > a').hover( function() { var link = $(this); menuLinkOnHover(link, link.next('.sub-menu')); }, function() { var link = $(this); menuLinkOnLeave(link, link.next('.sub-menu')); } ); $('ul ul.sub-menu a').hover( function() { var link = $(this); menuLinkOnHover(link, link.closest('.sub-menu')); }, function() { var link = $(this); menuLinkOnLeave(link, link.closest('.sub-menu')); } ); }); </script> </body> </html> |