One of my favorite features to build on a single page or documentation site is some kind of navigation that stays with the user as they scroll and highlights the section they are on.
These sidebars are great because they allow users to quickly see both what is in the article they are reading, and where they are in the article. You can see examples of these sidebars on the Bootstrap docs, the Ember-CLI docs, a fancy version with opening sub-navigation on The Express.JS docs and a horizontal version on Yesware's product page (see screenshots below - the "ready to give Yesware a test drive?" follows the user as he or she scrolls down the page).
I am going to show you how to build a sidebar from scratch. (Here's what it will look like when it's done. View the code here).
We will take it as a given that we already have a page written and it is restricted to a percentage of the page giving us room for the sidebar as well. First we need to add anchors above each section, which will serve as a place for the browser to jump to. These anchors will be created out of an 'a' tag directly above the sections outermost element. We are going to give the a tag a class of 'page-anchor', leave off the href and give it an id of some sort. The name of the id should be descriptive of the content it sits on top of. We will use these links to connect the anchor to the sidebar link that will bring you there. View Code here.
Next we have to build the sidebar. We will wrap this in a 'nav' element and give it an id of 'sidebar'. Inside the 'nav', we will give it a title as an 'h2' and then create the list itself. The list will be a 'ul' and can be styled however you like, but for this tutorial we're going with 'list-style-type: none;'. Each 'li' will wrap an 'a' tag. The href for these a tags is going to be '#' plus the id that you gave to the page anchor of the section it relates to. In example if link is for 'article1', the href would be '#article1'. Using an href in this format you can link to an element on the page that has an id and the browser will jump to that element on click. The name of the section will go inside this a tag. It is important that if there is a sub-list, you will put a ul underneath the a tag and create the sub-list the same as this list. View Code here. Now that we have the HTML for the sidebar, it's time to add some styles. What is important here is that we position the sidebar absolute, with a top position that will display the sidebar where we want when the page is loaded and the header is still in view. Then we are going to add styles for the sidebar with a class of 'fixed' which will in a fixed position with a top position where we want the sidebar when scrolling. View Code here. Next we are going to add some style to the links on our sidebar. With this we are also going to add some styles to the links with a 'current' class to differentiate what section the page is scrolled to. You can add any styles you like to this class, but for a minimal demo we will just change the color. View Code here.
Now that our markup is all done, its time to move into adding the JavaScript. I am going to use jQuery as it really cleans up the code and we don't need to worry much about cross-browser compatibility. The first thing we will do in the JavaScript is to create an array of the anchors that we have on the page. We do this by using the jQuery function on the selector for the class 'page-anchor'. Then we will use map on the returned object to create an array of objects. The object will have the anchor's height and the corresponding link. Since we have given our anchor an id, we will use jQuery to find the link that has an href attribute for that id, using jQuery attribute selector. View Code here.
Next we are going to assign of the length of this array to a variable I'm calling 'navLength'. This is done for performance reasons. View Code here. The rest of the script will take place within an event handler for the window scroll and will rely on the length of the navigation. By assigning it to a variable the browser only needs to calculate this value once and refer to that value, rather than every time window scroll is fired, which is constant during scrolling. The significance of this optimization is quite small, but it is good to be in the habit of making these optimization as performance in JavaScript can go south quickly with events firing excessively. Now we need to set the variable for the heart of all our window scroll interaction, the scroll position. This is done for the same reasons as setting 'nav'Length, only this variable is only good for the lifetime of the window scroll event, so it will have to live in the 'scroll' event handler. View Code here.
Next, we will loop through our anchors and check which section our browser is scrolled to. In order to do this we will check that the scrollPosition is greater than or equal to the anchors height, plus some padding. Since we only want to have one active link at a time, we need to find the lowest anchor that window is scroll below. To do this we are going to start our loop at the last item, and decrement our way back up checking the heights. Once we find a match, we are going to add a class to the item, remove the class from the others and exit the function by using a return. View Code here.
The one final change to make to the sidebar is to position it as fixed at a reasonable height when scrolled past the header. Since the loop that adds the current class exits with an explicit return, we must place this code above that iteration in the scroll event. Otherwise, the return would be hit, the scroll event would be exited and this code would never be executed. This is a similar concept to checking if the scrollPosition is below the anchor height, but instead we are checking if the page is scrolled below the header. When the page has been scrolled past the header, we will add the class fixed to the sidebar, and remove it for all other cases. View Code here.
Last but not least, what if the user re-sizes the window? All the anchor heights will be set incorrectly and the sidebar will stop working as expected. To combat this we are going to wrap all of our code in a window re-size event. Then finally we will make the function self calling so that it will fire when the file loads as well. This will leave our final JavaScript file looking like this. View Code here.
Congratulations, you've made a navigation sidebar that will stay in view and show the user where they are in the article. If you have any questions or comments, please leave a comment below. And if you're wondering about the content, it is coming from the two best lorem ipsum generators on the internet, Crazy Client Ipsum and Agency Ipsum! Check them out and consider them next time you're looking for filler text.