Skip to main content

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', and overflow = '' to release it.
  • It is cleaner to do this with a .no-scroll CSS class instead of inline styles.
  • If the page was already scrolled, plain overflow: hidden can make the content jump; position: fixed with top: -scrollY fixes that.
  • After unblocking, the offset is restored with window.scrollTo().
  • On iOS Safari you sometimes need a touchmove listener with { passive: false } and preventDefault().
  • The techniques combine: a class, plus the position freeze, plus the mobile safeguard.

Quick example

javascript
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>).

javascript
document.body.style.overflow = 'hidden'; // block scrolling

To put everything back:

javascript
document.body.style.overflow = ''; // unblock

This 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:

javascript
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:

css
/* styles.css */ .no-scroll { overflow: hidden; }
javascript
document.body.classList.add('no-scroll'); // block document.body.classList.remove('no-scroll'); // unblock

This 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:

javascript
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

GoalTechniqueNote
Just block itdocument.body.style.overflow = 'hidden'Fits the vast majority of cases
Preserve the offsetposition: fixed plus top = -scrollYNo jump when the lock is removed
Frequent useAdding a .no-scroll classConvenient for modals
Mobile reliabilitytouchmove plus preventDefault()For iOS Safari

Just remember: overflow: hidden blocks scrolling, position: fixed freezes the offset, touchmove with preventDefault() protects against touch scrolling on mobile.

Common mistakes

  • Applying overflow: hidden only 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: fixed without saving and later restoring scrollY: the user is thrown back to the top of the page.
  • Omitting width: 100% with position: fixed, so the layout narrows and the content jumps.
  • Registering touchmove without { passive: false }: the browser ignores preventDefault() and logs a warning.
  • Blocking touchmove on 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.