API Reference
Complete reference for all functions and types in the @hunterliu/scroll-lock library.
Functions
lockScroll(target, options?)
Locks scroll on the specified target element. Multiple calls on the same element use reference counting.
function lockScroll(
target: ScrollLockTarget,
options?: LockScrollOptions,
): LockState | undefined;Parameters:
target: ScrollLockTarget- Element to lock (default:document.bodyif null/undefined)options?: LockScrollOptions- Optional configuration objectreserveScrollBarGap?: boolean- Keep the room of the disappearing scrollbar, reserved by the first lock and restored by the last unlock (default:false). The body gets the width aspadding-right. Any other element keeps a stablescrollbar-gutterwhere the browser supports it, and otherwise gets the width as padding on its scrollbar's side, the left for a right-to-left element. Nothing is added when the gutter is already stable.
Returns:
LockState | undefined- The lock state object, orundefinedif not in browser environment
Example:
import { lockScroll } from "@hunterliu/scroll-lock";
// Lock body scroll (pass null/undefined for default)
lockScroll(document.body);
// Lock specific element
const modal = document.querySelector(".modal");
lockScroll(modal);
// Lock using window (targets documentElement)
lockScroll(window);
// Keep the layout still when the scrollbar disappears
lockScroll(document.body, { reserveScrollBarGap: true });unlockScroll(target, options?)
Unlocks scroll on the specified target element. Uses reference counting unless forced.
function unlockScroll(
target: ScrollLockTarget,
options?: {
force?: boolean;
},
): LockState | undefined;Parameters:
target: ScrollLockTarget- Element to unlock (default:document.bodyif null/undefined)options?- Optional configuration objectforce?: boolean- Bypass reference counting (default:false)
Returns:
LockState | undefined- The lock state object, orundefinedif not in browser environment
Example:
import { unlockScroll } from "@hunterliu/scroll-lock";
// Normal unlock (respects reference counting)
unlockScroll(document.body);
// Force unlock (ignores reference count)
unlockScroll(document.body, { force: true });
// Unlock specific element
unlockScroll(modal);isScrollLocked(target)
Checks if the specified target element is currently locked by this library.
function isScrollLocked(target: ScrollLockTarget): boolean;Parameters:
target: ScrollLockTarget- Element to check (default:document.bodyif null/undefined)
Returns:
boolean-trueif the element is locked,falseotherwise
Example:
import { isScrollLocked, lockScroll } from "@hunterliu/scroll-lock";
console.log(isScrollLocked(document.body)); // false
lockScroll(document.body);
console.log(isScrollLocked(document.body)); // true
// Check specific element
const modal = document.querySelector(".modal");
console.log(isScrollLocked(modal)); // falseclearAllScrollLocks()
Immediately clears all scroll locks on all targets, restoring their original styles.
function clearAllScrollLocks(): void;Example:
import { clearAllScrollLocks, lockScroll } from "@hunterliu/scroll-lock";
// Lock multiple elements
lockScroll(document.body);
lockScroll(document.querySelector(".modal"));
lockScroll(document.querySelector(".sidebar"));
// Clear all locks at once
clearAllScrollLocks();createScrollLock()
Creates an isolated instance with its own lock registry. Reference counts, isScrollLocked() and clearAllScrollLocks() of an instance only see the locks made through it.
function createScrollLock(): {
lockScroll: typeof lockScroll;
unlockScroll: typeof unlockScroll;
isScrollLocked: typeof isScrollLocked;
clearAllScrollLocks: typeof clearAllScrollLocks;
lockStateMap: WeakMap<HTMLElement | SVGElement, LockState>;
lockedElementSet: Set<HTMLElement | SVGElement>;
};Returns:
- The same functions and constants the package exports, bound to the new instance. The top-level exports are one shared instance created this way.
Example:
import { createScrollLock, lockScroll } from "@hunterliu/scroll-lock";
const drawerLocks = createScrollLock();
const drawer = document.querySelector(".drawer");
lockScroll(document.body);
drawerLocks.lockScroll(drawer);
console.log(drawerLocks.isScrollLocked(document.body)); // false
// Unlocks the drawer, body stays locked
drawerLocks.clearAllScrollLocks();WARNING
Instances do not know about each other, so lock a given element through one instance only. When two instances lock the same element, one releasing it can make it scrollable while the other still counts it as locked.
Types
ScrollLockTarget
Union type defining valid targets for scroll locking operations.
type ScrollLockTarget =
HTMLElement | SVGElement | Window | Document | null | undefined;Target Resolution:
HTMLElement | SVGElement- Used directly as the lock targetWindow- Targetswindow.document.documentElementDocument- Targetsdocument.documentElementnull | undefined- Defaults todocument.body
LockScrollOptions
Options accepted by lockScroll().
interface LockScrollOptions {
reserveScrollBarGap?: boolean;
}Properties:
reserveScrollBarGap?: boolean- Keep the room of the disappearing scrollbar (default:false). The body gets the width aspadding-right. Any other element keeps a stablescrollbar-gutterwhere the browser supports it, and otherwise gets the width as padding on its scrollbar's side, the left for a right-to-left element. Nothing is added when the gutter is already stable.
LockState
Interface representing the state of a locked element.
interface LockState {
count: number;
originalOverflowX?: string;
originalOverflowY?: string;
reservedScrollBarGap?: ReservedScrollBarGap;
stopTouchEventListener?: () => void;
stopWatchingStyle?: () => void;
}
interface ReservedScrollBarGap {
property: "scrollbar-gutter" | "padding-left" | "padding-right";
original: string;
value: string;
width: number;
}Properties:
count: number- Reference count for the lockoriginalOverflowX?: string- Original inline overflow-x valueoriginalOverflowY?: string- Original inline overflow-y valuereservedScrollBarGap?: ReservedScrollBarGap- Present whenreserveScrollBarGapkept the scrollbar's room: the property it set, its original inline value, the value it set and the scrollbar's widthstopTouchEventListener?: () => void- Function to cleanup the iOS touch event listenersstopWatchingStyle?: () => void- Function to stop watching the element's style for writes over the lock
Exported Constants
The state of the shared instance behind the top-level functions. An instance from createScrollLock() returns its own pair.
lockStateMap
WeakMap storing the lock state for each locked element.
export const lockStateMap: WeakMap<HTMLElement | SVGElement, LockState>;Usage:
import { lockStateMap, lockScroll } from "@hunterliu/scroll-lock";
lockScroll(document.body);
const state = lockStateMap.get(document.body);
console.log(state?.count); // 1lockedElementSet
Set containing all currently locked elements for iteration purposes.
export const lockedElementSet: Set<HTMLElement | SVGElement>;Usage:
import { lockedElementSet, lockScroll } from "@hunterliu/scroll-lock";
lockScroll(document.body);
console.log(lockedElementSet.has(document.body)); // true