Blocking page scroll
Page scrolling is blocked through CSS: you set overflow: hidden on <body> (or <html>), and the page stops scrolling both with the mouse wheel and with a finger. For a complete solution this trick is combined with freezing the scroll offset and with separate handling of mobile touch scrolling.
Theory
TL;DR
- The basic approach:
document.body.style.overflow = 'hidden', andoverflow = ''to release it. - It is cleaner to do this with a
.no-scrollCSS class instead of inline styles. - If the page was already scrolled, plain
overflow: hiddencan make the content jump;position: fixedwithtop: -scrollYfixes that. - After unblocking, the offset is restored with
window.scrollTo(). - On iOS Safari you sometimes need a
touchmovelistener with{ passive: false }andpreventDefault(). - The techniques combine: a class, plus the position freeze, plus the mobile safeguard.
Quick example
function disableScroll() {
document.body.style.overflow = 'hidden';
}
function enableScroll() {
document.body.style.overflow = '';
}
openModalBtn.addEventListener('click', disableScroll);
closeModalBtn.addEventListener('click', enableScroll);The simplest way: overflow: hidden
The browser lets you block scrolling of the whole page if you set overflow: hidden on <body> (or <html>).
document.body.style.overflow = 'hidden'; // block scrollingTo put everything back:
document.body.style.overflow = ''; // unblockThis is the most common approach: the page stays where it is, and neither the mouse wheel nor touch scrolling moves it. For the vast majority of interfaces (modals, burger menus, overlays) that is enough.
Preserving the offset so the content does not jump
If the page was scrolled and you simply apply overflow: hidden, the content can jerk because the scrollbar disappears.
Here is the more correct variant, with the position frozen:
function disableScroll() {
const scrollY = window.scrollY;
document.body.style.position = 'fixed';
document.body.style.top = `-${scrollY}px`;
document.body.style.width = '100%';
}
function enableScroll() {
const scrollY = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);
}Now the page stays exactly where the user stopped, and after unblocking it returns to the same point. width: 100% is needed because position: fixed takes <body> out of the flow, and it may shrink to the width of its content.
A CSS class instead of inline styles
Handy when you open modals often:
/* styles.css */
.no-scroll {
overflow: hidden;
}document.body.classList.add('no-scroll'); // block
document.body.classList.remove('no-scroll'); // unblockThis is clean, readable and easy to scale: all the locking rules live in one place in CSS, and JS only toggles the class.
Blocking scroll on mobile devices
On iOS, overflow: hidden sometimes does not help with touch scrolling inside <body>. In that case you intercept the touchmove event:
function preventTouchScroll(e) {
e.preventDefault();
}
function disableScrollMobile() {
document.addEventListener('touchmove', preventTouchScroll, { passive: false });
}
function enableScrollMobile() {
document.removeEventListener('touchmove', preventTouchScroll);
}The { passive: false } option is mandatory here: by default touchmove listeners on document are treated as passive, and preventDefault() inside them does nothing. This technique can be combined with overflow: hidden for reliability.
Summary table
| Goal | Technique | Note |
|---|---|---|
| Just block it | document.body.style.overflow = 'hidden' | Fits the vast majority of cases |
| Preserve the offset | position: fixed plus top = -scrollY | No jump when the lock is removed |
| Frequent use | Adding a .no-scroll class | Convenient for modals |
| Mobile reliability | touchmove plus preventDefault() | For iOS Safari |
Just remember:
overflow: hiddenblocks scrolling,position: fixedfreezes the offset,touchmovewithpreventDefault()protects against touch scrolling on mobile.
Common mistakes
- Applying
overflow: hiddenonly to<html>or only to<body>and being surprised that some browsers still scroll. Checking both elements is safer. - Forgetting to release the lock when the modal closes, especially on the error path: the page stays frozen forever.
- Using
position: fixedwithout saving and later restoringscrollY: the user is thrown back to the top of the page. - Omitting
width: 100%withposition: fixed, so the layout narrows and the content jumps. - Registering
touchmovewithout{ passive: false }: the browser ignorespreventDefault()and logs a warning. - Blocking
touchmoveon the whole document and killing scrolling inside the modal itself along with the page. Check the event target or attach the listener narrowly. - Treating the lock as a global boolean: with several modals you need a counter of open windows, otherwise closing one unlocks the page behind another.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.