MTJ Hax

Twitter Bootstrap’s Scrollspy Plugin Needs Better Docs

Posted in Uncategorized by mtjhax on February 11, 2013

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

Arrrrrrgh! I thought I followed their instructions to the letter. The 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 much trial-and-error I removed the data-target attribute and presto:

Click here to see the working example full-screen

This contradicts their documentation, so I poked around Stack Overflow looking for answers and I found many people reporting similar issues and not really getting decent answers. Usually they report that nothing happens with scrollspy or the last entry in the list is highlighted and will not change as you scroll. Some further digging uncovered this issue report on the Bootstrap GitHub page.

As it turns out, the Bootstrap folks are assuming 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:

    <body data-spy="scroll" data-target="#my-nav">
    <!-- WRONG -->
    <div>
      <ul id="my-nav" class="nav nav-list affix">
    <body data-spy="scroll" data-target="#my-nav">
    <!-- 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:

$('#navbar').scrollspy();

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 the code defaulting to use the first nav it finds in the page. Apparently, what I should have done from the start is just read the bootstrap.js source code and figured out what was going on — doing that tomorrow and will post an update.

By the way, the official response to this issue report was “good catch, why don’t you fix the docs and open a pull request” and the issue was closed. When I have time I will see if I can help out and not just complain…

UPDATE: After looking at the code it’s pretty obvious. To initialize ScrollSpy with JavaScript, the scrollspy() method should not be called on the target nav, but on the document body or container being scrolled. The documentation is not entirely clear on how to specify the target, because it assumes a familiarity with some common techniques and conventions. The target nav can be specified as an attribute similar to the way the offset is specified. The lack of explicit documentation is probably another oversight since offset is clearly documented. Example:

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

Another JSFiddle to prove that it works.