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 { createSignal } from 'solid-js';
import { scrollLock, useDraggable } from '@neodrag/solid';
function ScrollLockExamples() {
const [fullLock, setFullLock] = createSignal<HTMLElement | null>(
null,
);
const [horizontal, setHorizontal] =
createSignal<HTMLElement | null>(null);
const [container, setContainer] = createSignal<HTMLElement | null>(
null,
);
const [scrollArea, setScrollArea] =
createSignal<HTMLElement | null>(null);
useDraggable(fullLock, [scrollLock()]);
useDraggable(horizontal, [scrollLock({ lockAxis: 'x' })]);
useDraggable(container, [
scrollLock({ container: () => scrollArea()! }),
]);
return (
<div>
<div ref={setFullLock}>Drag me - page won't scroll</div>
<div ref={setHorizontal}>Vertical scrolling still works</div>
<div ref={setScrollArea} class="scroll-area">
<div ref={setContainer}>Lock specific container</div>
</div>
</div>
);
}
Dynamic Configuration
import { createSignal } from 'solid-js';
import {
scrollLock,
useDraggable,
createCompartment,
} from '@neodrag/solid';
function DynamicScrollLock() {
const [element, setElement] = createSignal<HTMLElement | null>(
null,
);
const [targetContainer, setTargetContainer] =
createSignal<HTMLElement | null>(null);
const [lockMode, setLockMode] = createSignal<
'both' | 'x' | 'y' | 'none'
>('both');
const [allowScrollbar, setAllowScrollbar] = createSignal(false);
const scrollLockComp = createCompartment(() => {
if (lockMode() === 'none') return null;
return scrollLock({
lockAxis: lockMode(),
allowScrollbar: allowScrollbar(),
container: () => targetContainer()!,
});
});
useDraggable(element, [scrollLockComp]);
return (
<div>
<div ref={setTargetContainer} class="scroll-container">
<div ref={setElement}>
Lock Mode: {lockMode()}
<br />
Scrollbar: {allowScrollbar() ? 'visible' : 'hidden'}
</div>
</div>
<div class="controls">
<label>
Lock Mode:
<select
value={lockMode()}
onChange={(e) => setLockMode(e.target.value as any)}
>
<option value="both">Both Axes</option>
<option value="x">X-Axis Only</option>
<option value="y">Y-Axis Only</option>
<option value="none">Disabled</option>
</select>
</label>
<label>
<input
type="checkbox"
checked={allowScrollbar()}
onChange={(e) => setAllowScrollbar(e.target.checked)}
/>
Allow Scrollbar
</label>
</div>
</div>
);
}
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.