Smooth Scroll behavior polyfill for browsers that don't natively support the CSS scroll-behavior property
npx @tessl/cli install tessl/npm-smoothscroll-polyfill@0.4.0Smoothscroll Polyfill provides a polyfill implementation of the Scroll Behavior specification, enabling smooth scrolling functionality in browsers that don't natively support the CSS scroll-behavior property. The polyfill detects native support and only activates when necessary, providing smooth scrolling behavior for scrollTo(), scrollIntoView(), and similar methods.
npm install smoothscroll-polyfillESM (ES Modules):
import smoothscroll from 'smoothscroll-polyfill';CommonJS:
const smoothscroll = require('smoothscroll-polyfill');Script tag (auto-executes):
<script src="https://unpkg.com/smoothscroll-polyfill/dist/smoothscroll.min.js"></script>import smoothscroll from 'smoothscroll-polyfill';
// Activate the polyfill
smoothscroll.polyfill();
// Now you can use smooth scrolling behavior
window.scrollTo({ top: 500, behavior: 'smooth' });
// Or on elements
document.getElementById('myDiv').scrollIntoView({ behavior: 'smooth' });The polyfill follows a conservative activation strategy:
Activates the smooth scroll polyfill by overriding native browser scroll methods.
/**
* Activates the smooth scroll polyfill
* Checks for 'scrollBehavior' in document.documentElement.style and only overrides
* native methods if it's not supported or if window.__forceSmoothScrollPolyfill__ is true
* When activated, overrides window.scroll, window.scrollBy, and Element prototype scroll methods
*/
function polyfill(): void;Once the polyfill is activated, these native browser methods are enhanced with smooth scrolling support:
Enhanced to support smooth scrolling behavior.
/**
* Scrolls the window to specified coordinates with optional smooth behavior
* @param {Object} options - Scroll options object with coordinates and behavior
* @param {number} [options.left] - Horizontal scroll position in pixels
* @param {number} [options.top] - Vertical scroll position in pixels
* @param {string} [options.behavior] - Scroll behavior: 'smooth', 'auto', or 'instant'
*/
window.scrollTo(options);
window.scroll(options);
/**
* Legacy signature: scrolls to specific coordinates
* @param {number} x - Horizontal scroll position in pixels
* @param {number} y - Vertical scroll position in pixels
*/
window.scrollTo(x, y);
window.scroll(x, y);Enhanced to support relative smooth scrolling.
/**
* Scrolls the window by specified amounts with optional smooth behavior
* @param {Object} options - Scroll options object with relative coordinates and behavior
* @param {number} [options.left] - Horizontal scroll offset in pixels
* @param {number} [options.top] - Vertical scroll offset in pixels
* @param {string} [options.behavior] - Scroll behavior: 'smooth', 'auto', or 'instant'
*/
window.scrollBy(options);
/**
* Legacy signature: scrolls by specific amounts
* @param {number} x - Horizontal scroll offset in pixels
* @param {number} y - Vertical scroll offset in pixels
*/
window.scrollBy(x, y);Element scroll methods are also enhanced with smooth scrolling support:
Enhanced element scrolling with smooth behavior support.
/**
* Scrolls element to specified coordinates with optional smooth behavior
* @param {Object} options - Scroll options object with coordinates and behavior
* @param {number} [options.left] - Horizontal scroll position in pixels
* @param {number} [options.top] - Vertical scroll position in pixels
* @param {string} [options.behavior] - Scroll behavior: 'smooth', 'auto', or 'instant'
*/
Element.prototype.scrollTo(options);
Element.prototype.scroll(options);
/**
* Legacy signature: scrolls element to specific coordinates
* @param {number} x - Horizontal scroll position in pixels
* @param {number} y - Vertical scroll position in pixels
*/
Element.prototype.scrollTo(x, y);
Element.prototype.scroll(x, y);Enhanced element relative scrolling with smooth behavior support.
/**
* Scrolls element by specified amounts with optional smooth behavior
* @param {Object} options - Scroll options object with relative coordinates and behavior
* @param {number} [options.left] - Horizontal scroll offset in pixels
* @param {number} [options.top] - Vertical scroll offset in pixels
* @param {string} [options.behavior] - Scroll behavior: 'smooth', 'auto', or 'instant'
*/
Element.prototype.scrollBy(options);
/**
* Legacy signature: scrolls element by specific amounts
* @param {number} x - Horizontal scroll offset in pixels
* @param {number} y - Vertical scroll offset in pixels
*/
Element.prototype.scrollBy(x, y);Enhanced to support smooth scroll behavior when bringing elements into view.
/**
* Scrolls element into view with optional smooth behavior
* @param {Object|boolean} [options] - Scroll into view options or boolean alignToTop
* @param {string} [options.behavior] - Scroll behavior: 'smooth', 'auto', or 'instant'
*/
Element.prototype.scrollIntoView(options?: {behavior?: string}): void;
/**
* Legacy signature: scrolls element into view
* @param {boolean} [alignToTop] - If true, aligns to top; if false, aligns to bottom
*/
Element.prototype.scrollIntoView(alignToTop?: boolean): void;Global variable to force the polyfill to override native implementations even when supported.
/**
* Global flag to force polyfill activation even when native support exists
* Must be set before loading/calling the polyfill
* @default undefined
*/
declare global {
interface Window {
__forceSmoothScrollPolyfill__?: boolean;
}
}Usage:
// Set before importing/loading the polyfill
window.__forceSmoothScrollPolyfill__ = true;
// Then load and activate
import smoothscroll from 'smoothscroll-polyfill';
smoothscroll.polyfill();interface ScrollToOptions {
left?: number;
top?: number;
behavior?: 'auto' | 'smooth' | 'instant';
}
interface ScrollIntoViewOptions {
behavior?: 'auto' | 'smooth' | 'instant';
}The polyfill handles several error conditions:
TypeError with message "behavior member of ScrollOptions [value] is not a valid value for enumeration ScrollBehavior." for unsupported behavior values (line 101-105)SyntaxError with message "Value could not be converted" when a single number is passed to element scroll methods without a second parameter (matching Firefox behavior, line 318-320)requestAnimationFrame and performance.now (needs polyfills for older browsers)window.getComputedStyle and getBoundingClientRectimport smoothscroll from 'smoothscroll-polyfill';
smoothscroll.polyfill();
// Smooth scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
// Smooth scroll by amount
window.scrollBy({ top: 200, behavior: 'smooth' });const container = document.getElementById('scrollable-container');
// Smooth scroll element content
container.scrollTo({ top: 100, left: 50, behavior: 'smooth' });
// Smooth scroll element into view
document.getElementById('target').scrollIntoView({ behavior: 'smooth' });// Set global flag before loading
window.__forceSmoothScrollPolyfill__ = true;
import smoothscroll from 'smoothscroll-polyfill';
smoothscroll.polyfill();
// Now all scroll methods use the polyfill implementation
window.scrollTo({ top: 500, behavior: 'smooth' });