At top and then fixed with animation so it's not clunky
This might be the solution you're looking for because it provides the fixed menu bar when scrolled out of the view, but when it switches from top to fixed, it does a slide-down animation, so it doesn't feel as you described it clunky.
HTML I've used in the example (simplified):
<div id="menu">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
...
</ul>
</div>
<div id="content" />
CSS is of course simple (only relevant styles)
#menu {
position: absolute;
width: 100%;
}
#menu.out {
position: fixed;
}
#menu ul {
margin: 0;
list-style: none;
}
#menu ul li {
display: inline-block;
}
And the script that does it and does so quickly (so it performs as fast as possible because the slowest part is the call to browser native getBoundingClientRect()
function which means it's still fast, very fast):
$(function() {
// save element references for faster execution
var menu = $("#menu");
var ul = menu.find("ul");
var content = $("#content")[0];
// get menu actual height
var menuHeight = menu[0].getBoundingClientRect().bottom;
// detect whether menu is scrolled out
var inView= true;
$(document).scroll(function(evt) {
evt.preventDefault();
var top = content.getBoundingClientRect().top;
var nextInView = (top + menuHeight) > 0;
// did state change so we have to change menu positioning
if (inView ^ nextInView)
{
inView = nextInView;
if (inView)
{
menu.removeClass("out");
}
else
{
menu.addClass("out");
ul.hide().slideDown("fast");
}
}
});
});
And this is it. You could also tweak the animation from slideDown()
to slide in
by animating top style property while you know exactly how many pixels above the view port you have to put the menu before the animation.
When you scroll the page and menu gets out of the view, it switches it to fixed position and scrolls the menu down so it dosesn't just jump in view but rather smoothly gets back in.