scrollLock
Prevent page scrolling while dragging
The scrollLock
plugin prevents page or container scrolling during drag operations. Essential for mobile devices and touch interfaces where dragging triggers unwanted scrolling.
scrollLock(); // Lock all scrolling
scrollLock({ lockAxis: 'x' }); // Only horizontal scrolling
scrollLock({ container: myContainer }); // Lock specific container
scrollLock({ allowScrollbar: true }); // Keep scrollbars clickable
Basic Usage
import { scrollLock, Draggable } from '@neodrag/vanilla';
const fullLock = document.getElementById('full-lock');
const horizontal = document.getElementById('horizontal');
const container = document.getElementById('container');
const scrollArea = document.getElementById('scroll-area');
new Draggable(fullLock, [scrollLock()]);
new Draggable(horizontal, [scrollLock({ lockAxis: 'x' })]);
new Draggable(container, [
scrollLock({ container: () => scrollArea }),
]);
Dynamic Configuration
import { scrollLock, Draggable, Compartment } from '@neodrag/vanilla';
const element = document.getElementById('draggable');
const container = document.getElementById('container');
const lockSelect = document.getElementById('lock-select');
const scrollbarToggle = document.getElementById('scrollbar-toggle');
const statusEl = document.getElementById('status');
let lockMode = 'both';
let allowScrollbar = false;
const scrollLockComp = new Compartment(() => {
if (lockMode === 'none') return null;
return scrollLock({
lockAxis: lockMode,
allowScrollbar: allowScrollbar,
container: () => container,
});
});
new Draggable(element, () => [scrollLockComp]);
function updateStatus() {
statusEl.textContent = `Lock Mode: ${lockMode}, Scrollbar: ${
allowScrollbar ? 'visible' : 'hidden'
}`;
scrollLockComp.current =
lockMode === 'none'
? null
: scrollLock({
lockAxis: lockMode,
allowScrollbar: allowScrollbar,
container: () => container,
});
}
lockSelect.addEventListener('change', (e) => {
lockMode = e.target.value;
updateStatus();
});
scrollbarToggle.addEventListener('change', (e) => {
allowScrollbar = e.target.checked;
updateStatus();
});
Common Use Cases
Mobile-Friendly Dragging
// Prevent mobile bounce scrolling
scrollLock({
lockAxis: 'both',
allowScrollbar: false, // Hide scrollbars on mobile
});
Modal/Overlay Dragging
// Lock body scrolling when dragging modal
scrollLock({
container: document.body,
lockAxis: 'both',
});
Horizontal Slider
// Allow vertical scrolling, prevent horizontal
scrollLock({
lockAxis: 'x',
allowScrollbar: true, // Keep vertical scrollbar
});
Canvas/Editor Tools
// Lock scrolling within canvas container
scrollLock({
container: () => document.getElementById('canvas-container'),
lockAxis: 'both',
});
Combining with Other Plugins
With Bounds
const plugins = [scrollLock(), bounds(BoundsFrom.viewport())];
Touch-Optimized Setup
const plugins = [
scrollLock({ lockAxis: 'both' }),
threshold({ distance: 5, delay: 100 }), // Prevent accidental drags
touchAction('none'), // Disable browser touch behaviors
];
Conditional ScrollLock
// Only lock on touch devices
const isTouchDevice = 'ontouchstart' in window;
const plugins = [
isTouchDevice ? scrollLock() : null,
bounds(BoundsFrom.parent()),
].filter(Boolean);
Platform Considerations
iOS Safari
// iOS requires specific handling
scrollLock({
lockAxis: 'both',
allowScrollbar: false, // Important for iOS
container: document.body,
});
Desktop vs Mobile
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
scrollLock({
lockAxis: isMobile ? 'both' : 'y', // More aggressive on mobile
allowScrollbar: !isMobile, // Keep scrollbars on desktop
});
How It Works
The scrollLock plugin prevents scrolling by temporarily modifying CSS properties:
During drag start:
- Stores original styles -
user-select
,touch-action
,overflow
- Applies lock styles:
user-select: none
- Prevents text selectionoverflow: hidden
- Hides scrollbars (ifallowScrollbar: false
)touch-action: pan-x/pan-y/none
- Controls touch scrolling direction
During drag end:
- Restores original styles - Everything returns to normal
Target selection:
- If
container
specified, applies to that element - If
container
isdocument.documentElement
or not specified, applies todocument.body
- Function containers are called dynamically each time
API Reference
function scrollLock(
options?: {
lockAxis?: 'x' | 'y' | 'both';
container?: HTMLElement | (() => HTMLElement);
allowScrollbar?: boolean;
} | null,
): Plugin;
Options:
lockAxis
- Which scrolling to prevent ('both'
default)'x'
- Only horizontal scrolling locked'y'
- Only vertical scrolling locked'both'
- All scrolling locked
container
- Where to apply scroll lock (document.documentElement
default)HTMLElement
- Specific element() => HTMLElement
- Dynamic element selection
allowScrollbar
- Keep scrollbars clickable (false
default)true
- Scrollbars remain visible and functionalfalse
- Scrollbars hidden viaoverflow: hidden
Returns: A plugin object for use with draggable.
Browser Support: All modern browsers, with special handling for iOS Safari touch behaviors.