Twitter Bootstrap’s Scrollspy Plugin Needs Better Docs

Posted in Uncategorized by mtjhax on February 11, 2013

UPDATE April 2018: At some point, my non-working example started working (in Chrome at least). Since I pinned the version of Bootstrap I was using, it seems likely that Chrome made some changes in DOM or JavaScript behavior. Leaving this here to document what I assume is still the correct way to implement Scrollspy.

I was trying to get Twitter Bootstrap’s Scrollspy plugin working so I can have a nice sidebar-nav menu like theirs that highlights automatically as you scroll the page. I created a JSFiddle here to demonstrate what I am trying to do.

Click here to see the non-working example full-screen

I thought I followed their instructions to the letter. The body tag contains attributes data-spy="scroll" to activate scrollspy and data-target="#my-nav" to indicate which nav I want to highlight to match the scrolled page. After some trial-and-error I removed the data-target attribute and presto:

Click here to see the working example full-screen

This seemed to contradict their documentation. Some digging uncovered this issue report on the Bootstrap GitHub page.

As it turns out, Bootstrap assumes that your nav menu will have a wrapper of some sort, like a div, and that the data-target attribute needs to reference the nav wrapper, not the nav itself:

    <!-- WRONG -->
      <ul id="my-nav" class="nav nav-list affix">
    <!-- CORRECT -->
    <div id="my-nav">
      <ul class="nav nav-list affix">

Click here to see the corrected example full-screen

Apparently, my original example worked when I removed the data-target attribute because it defaults to finding the first nav in the body. The instructions on how to activate scrollspy with JavaScript are very misleading. They suggest:


This implies that you should call the scrollspy() method on the nav object, but if you do it, it results in the behavior where the last entry in your nav menu is activated and the menu will not change as you scroll. I thought you needed to target the wrapper, as with the HTML attribute method, but this also does not work. If you use $(document.body).scrollspy() it seems to work, but again this seems to be defaulting to use the first nav it finds in the page.

After looking at the Bootstrap source code, it’s became obvious. To initialize ScrollSpy with JavaScript, the scrollspy() method should not be called on the target nav, but on the container being scrolled. The target nav can be specified as an attribute similar to the way the offset is specified. Example:

// using the not-explicitly-documented 'target' attribute
$('body').scrollspy({ target: '#my-nav' });

Another JSFiddle to prove that it works.