Bootstrap 3 Scrollspy

The Bootstrap 3 Scrollspy is probably one of the most frustrating jQuery plugins to implement. When you google for ‘Bootstrap Scrollspy’ a lot of the search results report something like “Scrollspy not working”.

In this tutorial I’ll show you how you can get it working correctly.

Bootstrap 3 Scrollspy

The Bootstrap Scrollspy spies a scroll-able area on a web page and activates a link in a list to reflect where (= at which item in the scroll-able area) the scrollbar is situated. You can also use the links to jump to a specific item in the scroll-able area. So it works in both directions: navigate to an item or show in navigation list which item is displayed in scroll-able area.

What the Scrollspy plugin does is keep track of the items in the scroll-able area and add a class “active” to the list item in the navigation for the item that’s currently displayed. When a users scrolls to the next item the class label is removed from the list item and the class label “active” is added to the next list item, etc.

Scroll-able area

The first requirement for the Scrollspy plugin is that you need (to create) a scroll-able area.

We can create a scroll-able area by setting a width and height too small to display all of the content. And we then set the value scroll for the overflow property.

Below you can see an example:

In this example I have created a div with an ID “scroll-able” that holds our content items. Each item is a section with a heading and a paragraph. The text in the paragraphs should obviously be longer (to display a shorter code example I have replaced a large part of each paragraph’s Lorem Ipsum text with “…”.

Then I have styled this div to become scroll-able: the width is set with a class col-md-6 (= 6 Bootstrap grid columns on a middle sized screen); the height is set to 400px and the overflow is set to scroll.


We then have to create the navigation that will both reflect the scroll position and can be used to jump to a certain item. This navigation must have the class nav otherwise the Scrollspy will not work (nav tags will not do the job, it must be a class).

The Bootstrap website shows a Scrollspy with a Navbar. In this tutorial I will show you two vertical examples. We begin with a simple unordered list:

For our navigation I have set the width to 3 grid columns (col-md-3). Depending on the names of the navigation items this width could be set to fewer or more columns. You can also see that we have inserted the required nav class into the opening ul tag.

Connection between navigation and scroll-able items

Each item in our scroll-able area should be linked to a list item in the navigation. In this example I have inserted href="#one", href="#two", etc. into the anchor tags for each list item. This corresponds with the ID‘s (“one”, “two”, etc.) I used for the sections.

A working Scrollspy

To get a working Scrollspy we now need to do 2 more things:

  1. Attach an ID to the navigation
  2. Add 2 data attributes to the scroll-able area

First we attach an ID to the navigation:

Make sure that you attached the ID to the containing element (in this instance: nav) that holds the element with the “nav” class. A misunderstanding is that you need simply use a ul with the class “nav” and add the ID to that. But you need to add the ID to the containing element.

We can then use this ID to link the scroll-able div to the navigation:

As you can see we use the data attribute data-target to link to the navigation ID.

Our second data attribute is data-spy. We give this a value “scroll”.

Some say that the Scrollspy only works if you insert these data attributes inside the opening body tag. But as you can see in this example you can also be more specific and apply it to a specific div.

Provided you didn’t forget to link to the required jQuery and Bootstrap JavaScript files than the Scrollspy should now be working.

However the navigation will probably not show that the Scrollspy really works. To remedy this we need to do something more.

Styling active list items

If we inspect our Scrollspy web page with a tool like Firebug we can see that it works:

Scrollspy activates list items - Firebug screenshot

So we know that the Scrollspy works but we do not see this on the web page. To correct this we have to style the active link:

I have styled the active list item with a blue background and the anchor inside this active list item gets a white color.

Scrollspy with List Group

The second example in this tutorial uses a List Group component. In the previous version of Bootstrap there was a Nav list component. But the Nav list component has been deprecated in Bootstrap 3. However we can use the List Group instead.

To style the navigation as a List Group we need to add a class list-group to the opening ul tag. And we need to add the class list-group-item to each list item. The rest of the code is the same as in the first example.
DEMO - Scrollspy with List Group

Using a script instead of data attributes

Our examples above relied on data attributes to get our Scrollspy to work. Instead we can also use a script:

The script is inserted just before the closing body tag and after the links used to include the jQuery and Bootstrap JavaScript libraries. The script first creates a jQuery object that holds the collections of all elements in the scroll-able div. It then attaches the scrollspy-method to this object. And this method targets the nav element with ID target_nav.

Don’t forget to delete the 2 data attributes (data-spy and data-target) if you started off with the markup for the previous code examples.

The offset

Another issue that often causes confusion is the offset option. On the Bootstrap website the description for the offset is: “Pixels to offset from top when calculating position of scroll.”.

I have googled this issue to find clarification. What I have read was among others: “It (= the offset) doesn’t do anything, but it is needed for proper spying.” ; “Bootstrap uses offset to resolve spying only, not scrolling.”.

After some trial and error I found that when I used an offset value that corresponds to the (top-)padding I set for the content inside the scroll-able area the result was very satisfactory. I checked to see if a different distance between the top of the window and the scroll-able area made any difference but found that this didn’t matter.

If you do not set the offset than the (default) value 10 is used.

Re-sizing the window might result in a less satisfactory correspondence between the scroll position and the activation of navigation list items. The same holds true if you use scripts to add and/or remove elements from the web page. Scrollspy creates an array (= list) with the position of elements after initialization. A re-size or the adding/removing of elements after the initialization makes this array/list incorrect. To remedy this situation you need to re-load the page( after a re-size of the window). And you should add a call for the refresh method to your script if your script adds and/or removes elements on the web page.


Andrew - March 22, 2015 Reply

Why is it that when I have the scroll-able area style it works, but when I take it away it stops working? I have enough content for a scroll bar so it should work.

The other thing that I can not get working is I want the navigation to stay at the top. I put an position: absolute style on it but it remains at the top of the page.

Theo - March 22, 2015 Reply

Hi Andrew,

the Scrollspy is actually intended for scroll-able areas that do not use the entire window. If it’s a long page in the entire window you want to scroll replace the Scrollspy with the Affix.

To have the navigation sit at the top of the window use:

ul.nav {position: fixed; top: 10px;}

Cheers, Theo

Divan - August 8, 2014 Reply

Great tutorial , thanks a lot Theo. Nice and simple

borgulas - June 21, 2014 Reply

Hey, great tut. Didnt get it working at first, but this helped me greatly. Works now 🙂

TeeravX - February 24, 2014 Reply

Can’t thank you enough for writing this. This tutorial is money. Like you said, scrollspy is a difficult bootstrap element to implement. Not only did you show how to do it, but you showed how to do it both via html tagging and javascript. Once again, thank you so much.

Theo - February 24, 2014 Reply

Thanks Travis, it’s great to hear that you really appreciate this tutorial.

Inder - February 4, 2014 Reply

I was really stuck on scrollspy for 3hrs. Your article solved my issue. Thanks a lot awesome tutorial.

Theo - February 4, 2014 Reply

Thanks Inder,

glad to hear that the tutorial helped you with scrollspy.

ludalex - October 15, 2013 Reply

Awesome tutorial.

Theo - October 15, 2013 Reply


Add your comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.