This project is currently archived. I unfortunately don't have the time to maintain it. Thanks to everyone that contributed along the way, it wouldn't have been possible without you.
scrollnav.js is a small (2.4kb gzipped), dependency free JavaScript plugin for auto generating single page navigation with active highlighting. Useful for creating a Table of Contents for a large document (think Wikis), navigation for a single page website, or anything else you might think of.
scrollnav works by scanning a block of content for section landmarks (typically heading elements) and generating a list of links from those landmarks. It then tracks the scroll location of the document and highlights the appropriate link. While previous versions injected wrappers within the content, the current version (ver 3) takes a much lighter approach, only changing the DOM as necessary. Visit the live demo at scrollnav.com to see for yourself.
To keep scrollnav small, default support starts with ES6 compatible browsers. To support ES5 compatible browsers you must provide your own polyfills or rely on a third party library like pollyfills.io. I personally use the following polyfill.io feature parameters to support scrollnav in IE 10 & 11.
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=default,NodeList.prototype.forEach,Array.prototype.includes"></script>
To add your own polyfills you will need to build the project from source.
The compiled, production ready plugin is available in the dist
directory.
Please don't use the src
directory unless you plan to build the entire
source.
<script src="[your assets directory]/scrollnav.min.umd.js"></script>
<script src="https://unpkg.com/scrollnav@3.0.2/dist/scrollnav.min.umd.js"></script>
Yarn: yarn add scrollnav
It's the new hotness, it's also better at managing dependencies than all it's predecesors.
NPM: npm install scrollnav
Good'ol NPM, it's always there, except when it isn't. Things have settled down a bit, but it was dicey there for a while. Even still, there's a reason even Yarn uses the NPM registry.
Bower: bower install scrollnav --save
The folks from Bower no longer recommend using Bower. Luckily they've provided a guide on how to migrate to Yarn. If you don't want to or can't migrate, scrollnav will continue to be available on Bower as long as it continues to run.
scrollnav works by scanning the given HTML Node Element for section
landmarks, by default h2
elements, that it then uses to generate the nav.
If we were to look at a typical document, it might look like this:
<div class="main-content">
<h2>First section</h2>
...
<h2>Second section</h2>
...
<h2>Third section</h2>
...
</div>
First, initialize scrollnav with the HTML Element. In this example we'll use
.querySelector()
but you could also use .getElementByID()
or
.getElementByClassName()
.
const content = document.querySelector('.main-content');
scrollnav.init(content);
scrollnav will then loop through the the h2
elements, add an ID if they don't
already have one, build the nav, and then inject it just before the content
Node. The result for our example document would look like this:
<nav class="scroll-nav">
<ol class="scroll-nav__list">
<li class="scroll-nav__item">
<a class="scroll-nav__link" href="#scroll-nav__1">
First heading
<a>
</li>
...
</ol>
</nav>
<div class="main-content">
<h2 id="scroll-nav__1">First Heading</h2>
...
</div>
To keep the plugin simple there are no styles added to the navigation, that's
all up to you (view the demo site for exmaples of the most common use
cases). The nav structure provides BEM Methodology class names for each
of the elements to provide consistent styling hooks (for a good overview read
MindBEMding - getting your head 'round BEM syntax). As the user scrolls
the document, scrollnav adds a scroll-nav__item--active
modifier for the
item's relative section that currently intersects with the activation
threshold (enable debug
mode to highlight the
threshold).
scrollnav includes some default settings that work for most situations, but if your project requires a bit more customization, scrollnav can most likely meet those. To modify either, pass in a single object (include settings and options as one object) as the second argument like this:
scrollnav.init(content, {
key: value
});
The following settings are editable to overwrite the default.
{
sections: 'selector',
// string
//
// Sets the querySelector for the content's section landmarks, by default
// it's 'h2'.
insertTarget: targetNode,
// HTML Node
//
// Sets the target Node for injecting the navigation, by default it's the
// content Node passed to scrollnav.
insertLocation: 'relativeLocation'
// string
//
// Sets the injection location relative to the insertTarget, by default it's
// 'before'.
//
// available options are 'append', 'prepend', 'after', or 'before'
easingStyle: 'easingName',
// string
//
// Sets the easing type for the scroll animation that is triggered by the
// click event on a nav item, by default it's 'easeOutQuad'.
//
// available options are 'linear' 'easeInQuad', 'easeOutQuad',
// 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic',
// 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint',
// 'easeOutQuint', easeInOutQuint
updateHistory: true
// boolean
//
// Sets the history behavior when a nav item is clicked, by default it's true
}
These additional options are editable but are not set by default.
{
subSections: '...',
// string
//
// Sets the querySelector for the content's sub-section landmarks.
onScroll: function() {...},
// function
//
// Sets the callback to be triggered after the window scrolls when triggered
// by the click event on a nav item.
onInit: function() {...},
// function
//
// Sets the callback to be triggered after the .init() method has completed.
onUpdatePositions: function() {...},
// function
//
// Sets the callback to be triggered after the .updatePositions() method
// has completed.
onDestroy: function() {...},
// function
//
// Sets the callback to be triggered after the .destroy() method has
// completed.
debug: false
// boolean
//
// Enables scrollnav's built in debug mode to log errors to the console and
// display the active area threshold on screen, helpful for when you've hit a
// snag you can't easily identify.
}
In addition to the .init()
method scrollnav provides two additional public
methods.
To remove the current instance of scrollnav call the destroy method. If you
need to trigger a callback after scrollnav has been removed, use the
onDestroy
option described above (passed either in the original init or with
the destroy method).
scrollnav.destroy();
scrollnav doesn't track outside DOM changes. If your page's content is dynamic
and updates after scrollnav is initialized you'll need to recalcuate the
position data with the updatePositions method. If you need to trigger a
callback after the position data has been recalculated, use the
onUpdatePositions
option described above (passed either in the original init
or with the updatePositions method).
scrollnav.updatePositions();
Please read and understand the Contributing Guidelines prior to opening an issue. Ensuring your issue conforms to the guidelines gives it a better chance I'll be able to help address it.
For questions about using scrollnav in your own project, your best bet is to post it to Stack Overflow. The community there is great at lending a hand and can often respond faster than I can, plus it becomes searchable for future developers who may run into the same question. If you're still stuck, please feel free to reach out to me to ask for help or clarification, I'm @jimmynotim on Twitter.
v3.0.2 is the current stable release. For detailed changes in each release please refer to the release notes. Please be sure you understand the changes before updating, v3 is a complete re-write of the plugin (as is v2 compared to v1 before it).
scrollnav is built and maintained by James Wilson (@jimmynotjim). I wouldn't be able to continue this project without a lot of help from the Open Source community. I welcome feedback and enhancements, but first, please make sure to read the Contributing Guide.
Thank you to everyone who has already contributed to scrollnav!
- Chris Garcia (@pixelbandito)
- Eric Clemmons (@ericclemmons)
- Felix Borzik (@Borzik)
- Jeff Byrnes (@jeffbyrnes)
- Jeff Coburn (@coburnicus)
- Jen Germann (@germanny)
- Jim Schmid (@sheeep)
- Marc Amos (@marcamos)
- Masud Rahman (@frutiger)
- Meghdad Hadidi (@MeghdadHadidi)
- Michael Benin (@michael-benin-CN)
- Rob Loach (@RobLoach)
- Thomas Guillary @thomasguillory
- Will Moore (@willthemoor)
- Wizcover (@wizcover)
scrollnav is Copyright © 2012-2018 James Wilson, released under the MIT license. This means you can re-create, edit or share the plugin as long as you maintain the same open licensing.