Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Lock and unlock a page's scroll position.

jquery.scrollLock.js

Useful for when a blocking user experience is needed (in my case, didn't want people unwittingly loosing their place by scrolling while a modal required their attention): $.scrollLock() locks the body in place, preventing scroll until it is unlocked.

// Locks the page if it's currently unlocked
$.scrollLock();

// ...or vice versa
$.scrollLock();

// Locks the page
$.scrollLock( true );

// Unlocks the page
$.scrollLock( false );
$.scrollLock = ( function scrollLockClosure() {
'use strict';
var $html = $( 'html' ),
// State: unlocked by default
locked = false,
// State: scroll to revert to
prevScroll = {
scrollLeft : $( window ).scrollLeft(),
scrollTop : $( window ).scrollTop()
},
// State: styles to revert to
prevStyles = {},
lockStyles = {
'overflow-y' : 'scroll',
'position' : 'fixed',
'width' : '100%'
};
// Instantiate cache in case someone tries to unlock before locking
saveStyles();
// Save context's inline styles in cache
function saveStyles() {
var styleAttr = $html.attr( 'style' ),
styleStrs = [],
styleHash = {};
if( !styleAttr ){
return;
}
styleStrs = styleAttr.split( /;\s/ );
$.each( styleStrs, function serializeStyleProp( styleString ){
if( !styleString ) {
return;
}
var keyValue = styleString.split( /\s:\s/ );
if( keyValue.length < 2 ) {
return;
}
styleHash[ keyValue[ 0 ] ] = keyValue[ 1 ];
} );
$.extend( prevStyles, styleHash );
}
function lock() {
var appliedLock = {};
// Duplicate execution will break DOM statefulness
if( locked ) {
return;
}
// Save scroll state...
prevScroll = {
scrollLeft : $( window ).scrollLeft(),
scrollTop : $( window ).scrollTop()
};
// ...and styles
saveStyles();
// Compose our applied CSS
$.extend( appliedLock, lockStyles, {
// And apply scroll state as styles
'left' : - prevScroll.scrollLeft + 'px',
'top' : - prevScroll.scrollTop + 'px'
} );
// Then lock styles...
$html.css( appliedLock );
// ...and scroll state
$( window )
.scrollLeft( 0 )
.scrollTop( 0 );
locked = true;
}
function unlock() {
// Duplicate execution will break DOM statefulness
if( !locked ) {
return;
}
// Revert styles
$html.attr( 'style', $( '<x>' ).css( prevStyles ).attr( 'style' ) || '' );
// Revert scroll values
$( window )
.scrollLeft( prevScroll.scrollLeft )
.scrollTop( prevScroll.scrollTop );
locked = false;
}
return function scrollLock( on ) {
// If an argument is passed, lock or unlock depending on truthiness
if( arguments.length ) {
if( on ) {
lock();
}
else {
unlock();
}
}
// Otherwise, toggle
else {
if( locked ){
unlock();
}
else {
lock();
}
}
};
}() );
$.scrollLock = ( function scrollLockSimple(){
var locked = false;
var $body;
var previous;
function lock(){
if( !$body ){
$body = $( 'body' );
}
previous = $body.css( 'overflow' );
$body.css( 'overflow', 'hidden' );
locked = true;
}
function unlock(){
$body.css( 'overflow', previous );
locked = false;
}
return function scrollLock( on ) {
// If an argument is passed, lock or unlock depending on truthiness
if( arguments.length ) {
if( on ) {
lock();
}
else {
unlock();
}
}
// Otherwise, toggle
else {
if( locked ){
unlock();
}
else {
lock();
}
}
};
}() );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment