I got an email quite a while back from Dirk Tucholski who showed me a site called FLOWmarket. He was wondering about how the menu system worked. I thought it looked neat and so set out to build it how I would do it. The idea is that there is a long vertical menu of links, not all of which are visible. As you scroll your mouse up and down the visible area, the menu scrolls itself to reveal more menu items and highlight the link the mouse is currently over.
HTML
A typical menu:
<div id="menu">
<ul>
<li><a href="#">Nature</a></li>
<li><a href="#">Receivability</a></li>
<li><a href="#">Alone time</a></li>
<!-- etc -->
</ul>
</div>
Starter CSS
We’re setting a static height, so let’s make sure the overflow value, for now, is set to overflow: auto;
That way the menu will scroll and be accessible regardless of JavaScript. Otherwise just basic styling.
#menu {
height: 360px;
overflow: auto;
}
#menu ul {
list-style: none;
}
#menu a {
text-decoration: none;
display: block;
color: black;
}
Starter JavaScript
The idea here is to wait for the menu links to be hovered over and adjust accordingly. We’ll apply a hover class for styling, then move an inside div (which we’ll append) up or down to acheive the effect. But just how far up or down? For that we’ll need to know exactly which link we’re hovering over. A higher position link (further down the list) needs to scroll the list farther than a lower position link. We’ll take this position and apply a speed multiplier to get the distance to offset. To get position, we’ll just loop through them and apply a data attribute.
$("#menu").css("overflow", "hidden").wrapInner("<div id='mover' />");
var $el,
speed = 13.5, // needs to be manually tinkered with
items = $("#menu a");
items
.each(function(i) {
$(this).attr("data-pos", i);
})
.hover(function() {
$el = $(this);
$el.addClass("hover");
$("#mover").css("top", -($el.data("pos") * speed - 40));
// 40 is the top padding for the fadeout
}, function() {
$(this).removeClass("hover");
});
Note that you can access HTML5 data attributes (e.g. <div data-pos=1>
) like $("div").data("pos");
which is succinct and cool.
Fadeouts
If you view the demo in a WebKit browser you’ll see the menu fade out on top and bottom as it scrolls. I just did that by using :before and :after pseudo elements absolutely positioned at the top and bottom of the menu which have white-to-transparent gradients.
#menu:before {
content: " ";
position: absolute;
top: 0;
left: 0;
height: 50px;
width: 100%;
z-index: 2;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255,255,255,100)),color-stop(1, rgba(255,255,255,0)));
}
#menu:after {
content: " ";
position: absolute;
bottom: 0;
left: 0;
height: 50px;
width: 100%;
z-index: 2;
background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255,255,255,0)),color-stop(1, rgba(255,255,255,100))); }
Use other browser prefixes as needed.
This “Speed” Business
The amount the menu is shifted up or down per menu item is affected by a speed multiplier. You can see in the above JavaScript that it’s set to a rather arbitrary looking 13.5. I played for a while trying to figure out a mathematical way to calculate the perfect speed multiplier based on number of menu items and heights and whatnot but didn’t get anything good. If you can think of something, let me know. It would definitely be better to not have to tinker with that multiplier every time the menu changes.
Keyboard Navigation
This menu, at the current speed, is a bit hard to navigate to an exact menu link you have picked out. To assist with that, we can add a little keyboard navigation. That is done by watching for keydown
events on the document
and firing off a function. To reduce rewriting code, we’ll just keep track of the currently active menu item, adjust it up or down depending on if the up arrow or down arrow was pressed, then trigger the mouseenter
or mouseleave
events accordingly, which are already set up to handle the menu functionality.
$(document).keydown(function(event) {
cur = $(".hover").attr("data-pos");
// Down arrow
if (event.keyCode == 40) {
$("[data-pos=" + cur + "]").trigger("mouseleave");
if (cur != max) { cur++; }
$("[data-pos=" + cur + "]").trigger("mouseenter");
}
// Up arrow
if (event.keyCode == 38) {
$("[data-pos=" + cur + "]").trigger("mouseleave");
if (cur > 0) { cur--; }
$("[data-pos=" + cur + "]").trigger("mouseenter");
}
});
The only bits not shown here is where we set up the cur
variable and set it in the hover function, but all that is there in the live demo.
Rabble rabble usability!
The very core of this idea, scrolling a menu faster than a user would expect, is bad for usability. It’s harder to pick out the exact link you are going for because of the unfamiliar and touchy movement. I am aware. The keyboard navigation helps somewhat there, but that’s also potentially an unfamiliar idea.
This isn’t a solution for every long menu in the world (also see here). This is more of a fun effect. Perhaps for a site where the menu isn’t all that super important. Perhaps as a way to display a poem. It’s a neat experience to use, so if that experience really works for the site, maybe a little accessibility hit is OK. I think the FLOWmarket site from whence this came is great.
This looks pretty cool Chris. I agree with you that there are definitely some usability concerns, but all-in-all, it’s a pretty decent technique.
Thanks for sharing!
Fun!
Assuming you had a an alphabetical list (obviously not in this example, but…), could you alleviate some of the usability by adding in an alphabet shortcut alongside, like the contacts list on iPhone?
I don’t know about anyone else, but that site performs like complete crap for me, nearly crashing the browser. Your demo works fine though.
it looks very nice, but the usability is crap :)
Pretty nice, but it’s a little … fast?
Looks like a slim version of this navigation:
http://www.queness.com/resources/html/scrollmenu/index.html
I’ll try to mix those to make it more smooth.
Thanks anyway : >
As us usual awesome effect.
Sorry for my silly question, but is it possible to achive this effect with css3 trasnsisions only ?
Thanks Chris!
I can’t think of a way off hand.
Thanks for your reply Chris.
imo it’s impossible to achieve this without using any dynamic variables, like mouse position, etc. ( You could try and use IEs “expressions” ;-) )
the closest i got to non-js approach was just enlarging hovered element and it’s adjacent sibling
li {
font-size:1em;
-webkit-transition:all .2s
}
li:hover {
font-size:2em
}
li:hover + li {
font-size:1.5em
}
‘”–>window.location=’http://www.google.com’
This is an interesting function.. I am not a js guru by any stretch of the imagination but I am surprised that there is not a way to slow down the scroll that way.
Today I chose to look at your site with FF 3.6.13 and when I look at the “keyboard navigation” I am getting some strange results. It seems to be choosing 2, 3 and sometimes even 4 of the items in the list when moving around by the up and down arrows.
I continue to be pleased and amazed at the help you give out here. Keep up the good work in the New Year!
I had the same issue with keyboard navigation, and it would be nice if #mover height adjusted automatically to margins, line height and speed.
This works much better in my opinion, and degrades into manual scroll bars when JavaScript is not enabled (for those of you with usability concerns):
http://valums.com/files/2009/vertical-menu/final.htm
I can appreciate the fact that this is a novelty item as far as CSS goes. But isn’t it important to teach people to create thing while maintaining best practices.
Oh well, nice post anyway Chris, despite the usability issues I got several good techniques to apply.
Thank you.
One of the details I like in the original and dont see back here is the ability to not only hover to the right of the list but also to the left of the list and have it also scroll.
A WebKit point: Suppose one has scrolled about half-way the list, the mouse leaves the list area, then comes back in from above – at this point I can’t hover nor click the first few items due to the overlay ‘fade’.
Really nice, Happy New Year Chris!
This is crap. Absolute useless crap.
Thats not what your mama said last night
I do’t know about the menu…maybe fewer items and slow it down a bit.
But what a clever site!!
Thanks for the idea and for sharing some thinking outside the “can.” :
I’m not sure how much people really want to get into a discussion of this. But, I will point out that it is a mistake to dismiss the usability of an idea based on the idea’s implementation. Think about making a button too small, or having a lot indistinguishable areas of a layout: neither make the idea of a button or layout a usability problem rather it is their implementation that becomes the issue. As other’s have already pointed out, speed, affordances etc can be tweaked to make this a very workable solution from a usability perspective.
Not as convinced about that I’m afraid, once you’re eyes are fixed on the option you intend to click, you move the mouse to go to it and it starts to move, yet the eye can’t easily follow its movement unless you move slowly, takes some getting used to when you try to use it for real.
Great article. Sorry to nit-pick, but “succicient” is not a real word. I think you meant “succinct”. :)
Thanks for the fix. Going to bury this comment because it’s now irrelevant.
re: speed and precision of movements…
Could you add a larger margin to the <li>s above and below the active item? That could make the margin-of-error a little more forgiving when trying to scroll only one or two items.
The menu is cool, but I agree that it is too fast. Perhaps it has other uses instead of menus. I will think about it.
Way, way too sensitive and touchy. It jumps two or three items at a time and extremely difficult to use. However, don’t know enough about the code, I guess, to figure out why or offer how to tweak it so it would be usable.
The html-element has a height of 100% in all examples cited here. It works well in this case, but problems come up when this scroll-list is used in a longer document. The list doesn’t scroll to its end. I could solve it in Webkit, but it didn’t work out in FF.
Its somewhat irritating selecting specific menu link on the theflowmarket site. And seems to be slippery… don’t know what you think
one word: unusable
Rabble rabble!
Yeah, read the whole article before calling somebody out!
I think it’s hilarious that almost every comment has basically been usability-bashing. Aside from the fact that Chris pointed out that he already knows, it’s surprising that very few are recognizing how neat a design element this could be.
The basic idea – giving a “stand-out” style to the item that is the user’s focus while not separating it from it’s place in the content – has great usability potential. It’s a textual equivalent of the Apple-style menus ( thing at the bottom ), and everybody seems to love those.
No, it’s not a finished product, and it’s not really ready to be used. (It is pretty close to the original implementation, however – any bashing should be pointed in the direction of FLOWmarket (they can handle it, they have plenty of canned Courage to Fail).) I really think it deserves development, though. I’ll be looking at it. I’ve always come here for cool ideas, rather than plug-and-play stuff (though I have found plenty of both – thanks, Chris!).
Very not-userfriendly
Wouldn’t something like this work better?
http://www.philosophydesign.com/examples/scroll.html
In this example I check where the mouse is currently positioned within the navigation and move the navigation position based on that. I havent added any of the fany styling but already to me this seems alot easier to select the menu item I want. Also you can add more or less menu items to the menu without having to alter any variables in the JS.
What do you think?
Pretty sweet menu, the other problem is mobile usability…that thing would be pretty impossible to use on a mobile phone I would bet.
Although I am not a big fan of minimalistic design, I liked the idea of this menu. Not sure if it’s Seo friendly, because of scripts being used…I bookmarked your site, it’s nice to learn some tricks from you :)
good shaning.
it is very good shaning