Scrollspy+sticky menu widget (Several bars)
This wiget is useful for having a top bar which will track several anchors while the user scrolls down
by jmeile
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/simple-scrollspy/2.4.2/simple-scrollspy.min.js"></script>
Some content here
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<div class="jm-menu" id="main-menu">
<a class="jm-menu-item active" href="#hero">Hero</a>
<a class="jm-menu-item" href="#section-1">Section 1</a>
<a class="jm-menu-item" href="#section-2">Section 2</a>
<a class="jm-menu-item" href="#section-3">Section 3</a>
Non existent
<a class="jm-menu-item" href="#section-4">Section 4</a>
<a class="jm-menu-item" href="#section-5">Section 5</a>
<a class="jm-menu-item" href="#section-6">Section_6</a>
</div>
<section class="jm-section jm-scrollspy" id="hero"><br/><br/><h3>Hero</h3>
On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.
</section>
<section class="jm-section jm-scrollspy" id="section-1"><br/><br/><h3>Section 1</h3>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
...
CSS
.jm-menu {
/* Avoid top and bottom margins for the menu; otherwise, you will get
* unexpected results when navigating to the different sections; mostly, the
* vertical space won't match what you are expecting. The problem is that
* these margins will overlap with the section margins. At best work with
* paddings or give margins to the content before the navigation bar and to
* the sections
*/
margin-top: 0px;
margin-bottom: 0px;
/* Top and bottom paddings are ok and they will be substracted */
padding-top: 5px;
padding-bottom: 5px;
border-color: #d3d3d3 !important;
border-bottom: 1px solid;
color: #d3d3d3 !important;
background-color: #fff;
}
/* The sticky class is added to the navbar with JS when it reaches its scroll position */
.jm-sticky {
position: fixed;
top: 0;
width: 100%;
background-color: #fff !important;
/* Some times, according to the font type, the previous sticky bar active
menu items will be shown (the bottom line); this will prevent this from
happening
*/
padding-bottom: 6px;
}
/* Add some top padding to the page content to prevent sudden quick movement (as the navigation bar gets a new position at the top of the page (position:fixed and top:0) */
.jm-sticky + .content {
padding-top: 60px;
}
.jm-menu-item {
margin-left: 5px;
margin-right: 5px;
padding-bottom: 4px;
font-size: 1rem;
}
.jm-menu-item,
.jm-menu-item:hover,
.jm-menu-item.active {
color: #000 !important;
text-decoration: none;
}
.jm-menu-item.active {
border-color: #000 !important;
border-bottom: 3px solid;
}
.jm-menu-item:hover {
border-color: #d3d3d3 !important;
border-bottom: 3px solid;
}
.jm-menu-item:first-child {
margin-left: 0px;
}
.jm-menu-item:last-child {
margin-right: 0px;
}
.jm-section {
padding-top: 1.5rem;
margin-top: 1.5rem;
}
.jm-section h3 {
/* Make sure that the first element comming in the section doesn't
* have any margin or padding at the top;...
JavaScript
//An array containing objects like:
// {
// id: html id of the dom element,
// dom: navbarDomObject,
// top: navbarTopOffset,
// height: navbarHeightOffset,
// currentHeightOffset: either height or zero according to stick status,
// verticalOffset: navbar.padding-bottom + navbar.padding-top,
// offsetFix: offset to hide the last parts of the letters after the
// base line
// }
var JMNavbars = [];
//The class used to stick navbars
var StickyClass;
//Query selector to your sections
var SectionClass;
//Element will be added active class
var MenuActiveTarget;
//Offset number
var StickyOffset;
//User Defined Scrolling Container
var ScrollContainer;
//Determines whether scrolling is instant or animates smoothly. This option is
//a string which must take one of the following values:
//* smooth: scrolling should animate smoothly
//* instant: scrolling should happen instantly in a single jump
//* auto: scroll behavior is determined by the computed value of scroll-behavior
var ScrollBehavior;
function jm_scrollspy_initialize(navbars, stickyClass, sectionClass, menuActiveTarget,
stickyOffset, scrollContainer, scrollBehavior)
{
var obj;
var navbar;
var navbarId;
var cssValue1;
var numValue1;
var cssValue2;
var numValue2;
for (let i = 0; i < navbars.length; i++) {
navbarId = navbars[i].id;
obj = {};
obj.id = navbarId;
navbar = document.getElementById(navbarId);
obj.dom = navbar;
obj.top = navbar.offsetTop;
obj.height = navbar.offsetHeight;
obj.currentHeightOffset = navbar.offsetHeight;
cssValue1 = window.getComputedStyle(navbar, null).getPropertyValue('padding-top');
numValue1 = parseFloat(cssValue1);
cssValue2 = window.getComputedStyle(navbar, null).getPropertyValue('padding-bottom');
numValue2 = parseFloat(cssValue2);
obj.verticalOffset = numValue1 + numValue2;
obj.offsetFix = navbars[i].offsetFix;
JMNavbars.push(obj);
}
...