How to Create a Walking Navigation
Posted on February 16, 2012 and got 6 shouts so farOn a single page website with fixed position navigation, it will be nice to tell user on what section they are reading at. In this tutorial I am going to share how to create a walking navigation, on the other words, auto focus navigation based on user scrolling, by taking advantage of both jQuery and CSS animation.
The Trick
We can estimate which section the users are reading at by checking their current scroll position and get closest section from it. This mean we have to separate each section by element that we can identify as a different section. To make us ease on creating an auto focus on the navigation related to the section user reading at, we can put the same identity on section and navigation, respectively.

Building The Page
The single page we want to create consists of navigation and section. Each of them has five items, home, about, blog, contact, credits. Home on navigation related to home on section, so on. Each item on navigation identified using href attribut which is the same value as section that identified by id attribute.
Walking Navigationcontentcontentcontentcontentcontent
#header-wrapper {
display: block;
position: fixed;
width: 100%;
height: 60px;
color: #fff;
background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#cbcbcb));
background-image: -webkit-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -moz-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -ms-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -o-linear-gradient(top, #fefefe, #cbcbcb);
background-image: linear-gradient(to bottom, #fefefe, #cbcbcb);
box-shadow: rgba(214,214,214,0.6) 0px 6px 0px;
color: #444;
text-shadow: rgba(255,255,255,0.5) 1px 1px 0px;
}
#content-wrapper {
display: block;
padding-top: 5em;
color: #414141;
line-height: 2em;
text-shadow: #fff 1px 1px 0px;
font-size: 15px;
}
#home, #about, #blog, #tweet, #contact {
display: block;
}
.wrapper, #content-wrapper {
margin: 0 auto;
width: 960px;
text-align: left;
}
.navigation {
font-size: 13px;
text-transform: uppercase;
display: inline-block;
margin: 0;
list-style-type: none;
vertical-align: top;
}
.navigation li {
display: inline-block;
width: 70px;
height: 16px;
padding: 22px 0;
text-align: center;
cursor: pointer;
}
.navigation li:hover, .selected-nav {
background-image: -webkit-gradient(linear, left top, left bottom, from(#7bbbf7), to(#338cdf));
background-image: -webkit-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -moz-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -ms-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -o-linear-gradient(top, #7bbbf7, #338cdf);
background-image: linear-gradient(to bottom, #7bbbf7, #338cdf);
border-bottom: 6px solid #1f6db6;
}
.navigation li a {
position: relative;
z-index: 1px;
color: #444;
text-decoration: none;
}
.navigation li:hover a, .selected-nav a, .selected-nav2 a, .top a {
color: #fff !important;
text-shadow: #335f87 1px 1px 0px;
}
Notice that the header-wrapper has position fixed, this aim to make the navigation always follow user scroll through the page.

Simple Style
.scroll() is a jQuery API to read user’s scroll on an element, in this context we will bind them to the window element. After reading the scroll we check its current position relative to the top of the page (scrollPosition) , checking through each navigation’s item and check the section which is closest to the scrollPosition. If the section found add customized style to make it look like selected.
//Bootstraping variable
headerWrapper = parseInt($('#header-wrapper').height());
offsetTolerance = 40;
//Detecting user's scroll
$(window).scroll(function() {
//Check scroll position
scrollPosition = parseInt($(this).scrollTop());
//Move trough each menu and check its position with scroll position then add selected-nav class
$('.navigation a').each(function() {
thisHref = $(this).attr('href');
thisTruePosition = parseInt($(thisHref).offset().top);
thisPosition = thisTruePosition - headerWrapper - offsetTolerance;
if(scrollPosition >= thisPosition) {
$('.selected-nav').removeClass('selected-nav');
$('.navigation a[href='+ thisHref +']').parent('li').addClass('selected-nav');
}
});
});
If the last section is too close to the bottom of the page we can’t read the section using previous technique, to make it works we have to check we’re at the bottom of the page by subtract document height with window height then compare with scrollPosition, if it equal or scrollPosition more than bottomPage then we’re at the bottom of the page
//If we're at the bottom of the page, move pointer to the last section
bottomPage = parseInt($(document).height()) - parseInt($(window).height());
if(scrollPosition == bottomPage || scrollPosition >= bottomPage) {
$('.selected-nav').removeClass('selected-nav');
$('.navigation a:last').parent('li').addClass('selected-nav');
}

Check out the demo 1 for this code.

Sliding Style
Another slick way to tell the user is by sliding a pointer through the navigation. From previous code we need to add an image element acting as pointer. This pointer will be move to the left or to the right pointing current section.
//Bootstraping variable
headerWrapper = parseInt($('#header-wrapper').height());
offsetTolerance = 40;
pointer = $('.pointer');
firstNav = $('.navigation a:first').parent('li');
defaultPointer = firstNav.offset().left;
//Move pointer to the first menu
pointer.css('left', defaultPointer);
firstNav.addClass('selected-nav2');
//Detecting user's scroll
$(window).scroll(function() {
//Check scroll position
scrollPosition = parseInt($(this).scrollTop());
//Move trough each menu and check its position with scroll position then move the pointer
$('.navigation a').each(function() {
thisHref = $(this).attr('href');
thisTruePosition = parseInt($(thisHref).offset().top);
thisPosition = thisTruePosition - headerWrapper - offsetTolerance;
thisNav = $('.navigation a[href='+ thisHref +']').parent('li');
currentPosition = parseInt(thisNav.offset().left);
if(scrollPosition >= thisPosition) {
$('.selected-nav2').removeClass('selected-nav2');
pointer.stop().animate({'left': currentPosition});
thisNav.addClass('selected-nav2');
}
});
//If we're at the bottom of the page, move pointer to the last section
bottomPage = parseInt($(document).height()) - parseInt($(window).height());
lastNav = $('.navigation a:last').parent('li');
currentPosition = lastNav.offset().left;
if(scrollPosition == bottomPage || scrollPosition >= bottomPage) {
$('.selected-nav2').removeClass('selected-nav2');
pointer.stop().animate({'left': currentPosition});
lastNav.addClass('selected-nav2');
}
});
The difference with previous code is we are playing on the pointer rather than only styling the navigation. We check through each navigation’s item and get selected item’s left position relative to the window (currentPosition), finally slide the pointer to the currentPosition by adjusting its left. Check out the demo 2 for this code.

Cube Style
If you read my previous post you have to be familiar with this style. On this style focusing the navigation is using cube rotation, up to down direction. Just like previous post, we have to modify each navigation’s item by adding front side and top side. Front side is the default style and top side is selected style.
.cube {
-webkit-perspective : 1200px;
-moz-perspective : 1200px;
-ms-perspective : 1200px;
perspective : 1200px;
padding: 0 !important;
}
.cube:hover a {
background: 0;
color: 0;
text-shadow: 0;
}
.cube-wrapper-show {
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-animation: showMenu 0.5s ease-in-out;
-moz-animation: showMenu 0.5s ease-in-out;
-ms-animation: showMenu 0.5s ease-in-out;
height: 60px;
}
.cube-wrapper-hide {
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-animation: hideMenu 0.5s ease-in-out;
-moz-animation: hideMenu 0.5s ease-in-out;
-ms-animation: hideMenu 0.5s ease-in-out;
height: 60px;
}
.top, .front {
display: block;
width: 70px;
height: 16px;
padding: 22px 0;
vertical-align: middle;
position: absolute;
}
.front {
background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#cbcbcb));
background-image: -webkit-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -moz-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -ms-linear-gradient(top, #fefefe, #cbcbcb);
background-image: -o-linear-gradient(top, #fefefe, #cbcbcb);
background-image: linear-gradient(to bottom, #fefefe, #cbcbcb);
}
.front-before-rotate {
box-shadow: rgba(214,214,214,0.6) 0px 6px 0px;
-webkit-transform: translateZ(25px);
-moz-transform: translateZ(25px);
-ms-transform: translateZ(25px);
transform: translateZ(25px);
}
.top {
background-image: -webkit-gradient(linear, left top, left bottom, from(#7bbbf7), to(#338cdf));
background-image: -webkit-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -moz-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -ms-linear-gradient(top, #7bbbf7, #338cdf);
background-image: -o-linear-gradient(top, #7bbbf7, #338cdf);
background-image: linear-gradient(to bottom, #7bbbf7, #338cdf);
border-bottom: 6px solid #1f6db6;
-webkit-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
-moz-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
-ms-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
transform: rotate3d(1,0,0,90deg)
translateZ(30px);
}
.hide-front {
display: none;
}
.show-front {
display: block;
}
.show-top {
display: block;
}
.showed-top {
display: block;
-webkit-transform: rotate3d(1,0,0,0deg);
-moz-transform: rotate3d(1,0,0,0deg);
-ms-transform: rotate3d(1,0,0,0deg);
transform: rotate3d(1,0,0,0deg);
}
.hidden-top {
-webkit-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
-moz-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
-ms-transform: rotate3d(1,0,0,90deg)
translateZ(30px);
transform: rotate3d(1,0,0,90deg)
translateZ(30px);
}
@-webkit-keyframes showMenu {
0% { -webkit-transform: rotate3d(1,0,0,0); }
100% { -webkit-transform: rotate3d(1,0,0,-90deg); }
}
@-webkit-keyframes hideMenu {
0% { -webkit-transform: rotate3d(1,0,0,-90deg); }
100% { -webkit-transform: rotate3d(1,0,0,0); }
}
@-moz-keyframes showMenu {
0% { -moz-transform: rotate3d(1,0,0,0); }
100% { -moz-transform: rotate3d(1,0,0,-90deg); }
}
@-moz-keyframes hideMenu {
0% { -moz-transform: rotate3d(1,0,0,-90deg); }
100% { -moz-transform: rotate3d(1,0,0,0); }
}
@-ms-keyframes showMenu {
0% { -ms-transform: rotate3d(1,0,0,0); }
100% { -ms-transform: rotate3d(1,0,0,-90deg); }
}
@-ms-keyframes hideMenu {
0% { -ms-transform: rotate3d(1,0,0,-90deg); }
100% { -ms-transform: rotate3d(1,0,0,0); }
}
After checking through each navigation’s item, we add cube-wrapper-show class that trigger the CSS animation to rotate the cube-wrapper and hide other items by adding cube-wrapper-hide. When the animation done we push the displayed side to be displayed (see the reason).
//Bootstrapping variable
headerWrapper = parseInt($('#header-wrapper').height());
offsetTolerance = 40;
allNavigation = $('.section');
arrayNavigation = new Array;
bottomPage = parseInt($(document).height()) - parseInt($(window).height());
//Get all menu based on section
allNavigation.each(function() {
thisNav = $(this).attr('id');
thisHref = $('a[href=#'+thisNav+']').attr('href');
thisTruePosition = parseInt($(thisHref).offset().top);
thisPosition = thisTruePosition - headerWrapper - offsetTolerance;
arrayNavigation.push(thisPosition);
});
//Detecting user's scroll
$(window).scroll(function() {
//Check scroll position and get displayed section
scrollPosition = parseInt($(this).scrollTop());
thisPosition = 0;
for(var i=0; i < arrayNavigation.length; i++){
if(scrollPosition >= arrayNavigation[i]) {
thisPosition = i;
}
}
//If we're at the bottom of the page, select last section
if(scrollPosition == bottomPage || scrollPosition >= bottomPage) {
thisPosition = parseInt(arrayNavigation.length) - 1;
}
//Preparing menu to be displayed
currentElem = $('.cube:eq('+thisPosition+')');
currentCube = currentElem.children('.cube-wrapper');
currentFront = currentCube.children('.front');
currentTop = currentCube.children('.top');
//Get last displayed menu
showedElem = $('.cube:lt('+thisPosition+'), .cube:gt('+thisPosition+')');
showedCube = showedElem.children('.cube-wrapper');
showedFront = showedCube.children('.front');
if(!currentCube.hasClass('cube-wrapper-show')) {
//Hide other displayed menu
showedCube.each(function() {
if($(this).hasClass('cube-wrapper-show')) {
$(this).children('.front').removeClass('hide-front');
$(this).removeClass('cube-wrapper-show').addClass('cube-wrapper-hide');
$(this).children('.top').removeClass().addClass('top');
}
});
//Show selected menu
currentFront.addClass('front-before-rotate');
currentCube.addClass('cube-wrapper-show');
//Clean up after animation end
if($.browser.webkit) {
currentCube.bind('webkitAnimationEnd', function() {
//Clean up last displayed menu
showedCube.removeClass().addClass('cube-wrapper');
showedFront.removeClass('front-before-rotate');
//Force menu to be displayed
currentTop.addClass('showed-top');
currentFront.addClass('hide-front');
$(this).unbind();
});
} else {
setTimeout(function(){
showedCube.removeClass().addClass('cube-wrapper');
showedFront.removeClass('front-before-rotate');
//Force menu to be displayed
currentTop.addClass('showed-top');
currentFront.addClass('hide-front');
}, 500);
}
}
});
Check out the demo 3 for this code.

We’re Done
Ton of single page website with their unique menu spread out there, all of them can inspire us the navigation design and behavior, maybe you have one? Share yours or your thought!

What They Shouts
Shout Something
The Artist
Popular Post
Stuff & Community News
Tags Collection
saran: gimana kalo pas diklik menu di header, kontennya sliding..
terus juga, kalo di contoh 1, bagian contact gak pernah dapet. soalnya nanggung keatas kena blog, kebawah kena credits.
begitupun pas klik menu contact. langsung ke credits.
tadinya pengen komentar pake bahasa inggris, tapi terlalu ribet.
))
btw, idenya bagus..
Mantab mam :-bd
Emang tadinya mau ditambah sliding ke konten tapi gw pikir jadi gak fokus pembahasannya
I’ve seen a jQuery plugin called “ScrollSpy” from Bootstrap working in a similar way. Thanks
There is also a jquery plugin called “Waypoint” that you can use to achieve this. But also much more.
Very nice and very useful example. Thanks.
super effects
thank you