Twitter Bootstrap’s Scrollspy Plugin Needs Better Docs
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 --> <div> <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:
$('#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 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' });
Thanks for this. You’ve explained behaviour that I was seeing, but didn’t know why!
Thank you so much. You just made my day!! Bootstrap documentation sucks
Well, “sucks” is a bit harsh. I might say it is just a bit terse. Surprised there aren’t more introductory guides to using bootstrap.js online.
Good man, exactly what I was after. Didn’t want the attributes on the body tag, but the examples they give do not show how to do this properly. Thanks.
Just had this problem and fixed it all up. There is a lot of stuff lacking in the documentation. One other thing they do not talk about is the scroll box resizing because as your scroll it leaves the span. So what they did behind the scenes was give each box a set width.
Lots of little undocumented tricks!
thanks a lot for sharing this – I found out that my problem was that scrollspy looks for the class “nav”. Reading the source is a good idea^^
Thanks for the clarifications! Much appreciated :)
This helped me out a ton as well! – THANK YOU. :)
Additional things I needed: the comments with the # broke my code (use // instead).
You need the javascript OR data-attribute method, not both.
I used the javascript method for scrollspy and the data attributes data-spy=”affix”, data-offset-top=””, data-offset-bottom=”” to get the affix working.
If you’re affix-bottom is flickering, it needs a position:relative on one of its parent divs.
You need to set the .affix’s top and the .affix-bottom bottom on your own, it is not preset.
Putting Ruby comments in JavaScript and HTML examples… facepalm. Fixed. Thanks for the affix tips!
Hey man, thanks a lot!!
I was almost giving up haha.
Hugs from Brazil!
You saved my life Sir ! I had a very strange behaviour with my nav, items were twinkling between active and non-active state, when I was using the scroll.
I first thought it was due to a bad value of the ‘offset’ JavaScript option : it was the only parameter described in the doc !
Thanksfully I found your article and tried to use the ‘target’ option… Now it works like a charm !
Thank goodness for this post! I was totally confused as to what I was doing wrong! My main problem was that I had the class I was spying on , on the nav element, not the wrapper. Once I moved things it worked. PHEW! thanks!!!
I had exactly the same issue and to fix it all I did was correctly declare my doctype “” and it was fixed. I added your code and it still seemed to only select the bottom item. Then when I fixed my DTD it worked so I then removed your JS code and it still worked. I think the reason why your JSFiddle works is because the DTD is already declared for you.
Just my contribution.
Thanks
Ant
I didn’t fix my issue with JavaScript. I fixed it by moving
id="my-nav"
from theul
tag to the wrapperdiv
, so thedata-target
attribute referenced the correct element. This link is an example of a JSFiddle with no JavaScript that causes the problem, and this one is an example of the problem resolved by using the properdata-target
. My JavaScript examples were just to show how the Bootstrap docs were a little misleading and missing an explicit description of thetarget
attribute. However, it is very interesting to know that an incorrect doctype can cause similar problems.Finally, a fantastic write up on this. Thank you!
This was very helpful for figuring out Bootstrap scrollspy affix issues. I notice you add the class “affix” to the unordered list. I’m pretty sure adding data-spy=”affix” to the direct parent to the UL will apply all three affix classes appropriately. That’s what I’ve concluded. They need a layman who knows Bootstrap inside out to write their docs :) Thanks for this, you made me find my mistake
Another cool tip, thanks Ryan.