micro-scrollspy
Version:
A lightweight library for implementing scrollspy functionality in web applications.
91 lines (75 loc) • 2.9 kB
JavaScript
// Import dependencies
import scrollama from 'scrollama'; // For scroll events
import throttle from 'lodash.throttle'; // To throttle scroll events
import delegate from 'delegate'; // For easy event delegation
// Polyfills
import 'intersection-observer'; // Polyfill for intersection observer
import smoothscroll from 'smoothscroll-polyfill'; // Polyfill for smooth scrolling
// Initialize polyfills
smoothscroll.polyfill();
class LightweightScrollSpy {
/**
* Initializes the scrollspy with options
* @param {Object} options - Configuration options for the scrollspy
*/
constructor(options) {
// Default options
this.defaultOptions = {
sections: 'section', // Selector for the sections
navLinks: 'nav a', // Selector for the navigation links
activeClass: 'active', // Class to be added to the active link
offset: 0.5, // Intersection observer offset (viewport ratio)
};
// Extend default options with user-provided options
this.options = { ...this.defaultOptions, ...options };
// Initialize scrollama
this.scroller = scrollama();
this.init();
}
/**
* Sets up the scrollama instance and event listeners
*/
init() {
// Scrollama setup
this.scroller.setup({
step: this.options.sections, // Elements to observe
offset: this.options.offset, // Trigger offset
})
.onStepEnter(this.handleStepEnter.bind(this))
.onStepExit(this.handleStepExit.bind(this));
// Throttle scroll event to improve performance
window.addEventListener('scroll', throttle(this.handleScroll.bind(this), 200));
// Delegate click event for navigation links to enable smooth scroll
delegate(this.options.navLinks, 'click', this.handleNavLinkClick.bind(this));
}
/**
* Handles entering a section
* @param {Object} response - The response object from scrollama
*/
handleStepEnter(response) {
// Remove active class from all links
document.querySelectorAll(this.options.navLinks).forEach(link => {
link.classList.remove(this.options.activeClass);
});
// Find the navigation link corresponding to the section
const activeLink = document.querySelector(`[href="#${response.element.id}"]`);
// Add active class to the corresponding link
if (activeLink) {
activeLink.classList.add(this.options.activeClass);
}
}
/**
* Handles navigation link clicks to smoothly scroll to the section
* @param {Event} event - The click event
*/
handleNavLinkClick(event) {
event.preventDefault();
const targetId = event.delegateTarget.getAttribute('href').substring(1);
const targetSection = document.getElementById(targetId);
// Use the smooth scroll polyfill to scroll to the section
if (targetSection) {
targetSection.scrollIntoView({ behavior: 'smooth' });
}
}
}
export default LightweightScrollSpy;