Back

How to Stop a Page From Scrolling While a Dialog Is Open

How to Stop a Page From Scrolling While a Dialog Is Open

Opening a modal dialog should focus the user’s attention on that dialog—not let them scroll the page behind it. Yet preventing background scrolling remains surprisingly difficult, especially when you need reliable iOS Safari scroll lock behavior.

The <dialog> element with showModal() handles interaction blocking automatically. The browser marks everything outside the dialog as inert, preventing clicks and keyboard navigation. But scroll prevention? That’s a separate problem the platform doesn’t fully solve.

This article covers the main approaches to scroll lock modal dialog implementations, their trade-offs, and why no single solution works perfectly everywhere.

Key Takeaways

  • The showModal() method blocks interaction but doesn’t prevent background scrolling
  • CSS overflow: hidden works on desktop but fails on iOS Safari
  • The fixed body technique with scroll position restoration provides the most reliable cross-browser solution
  • Use overscroll-behavior: contain alongside other methods to prevent scroll chaining
  • Always keep your dialog scrollable with max-height and overflow-y: auto for accessibility

What showModal() Does and Doesn’t Do

When you call dialog.showModal(), the browser:

  • Displays the dialog in the top layer
  • Creates a ::backdrop pseudo-element
  • Makes background content inert (blocks pointer and keyboard interaction)
  • Traps focus within the dialog

What it doesn’t do: reliably prevent background scrolling across all browsers and devices. On some platforms—most notably mobile—users can still scroll the page using touch gestures or scroll wheels.

The Simple CSS Approach: overflow: hidden

The most common solution uses the :has() selector to detect an open dialog:

body:has(dialog[open]) {
  overflow: hidden;
}

This works on most desktop browsers. The page becomes non-scrollable while the dialog is open.

The catch: This approach is unreliable on iOS Safari. Touch scrolling often bypasses overflow: hidden on the body, allowing users to scroll the background anyway. If your audience includes mobile Safari users, you’ll need something more robust.

The Fixed Body Technique for Cross-Browser Reliability

The most reliable cross-browser solution—including iOS Safari scroll lock—uses JavaScript to fix the body in place:

let scrollPosition = 0;

function lockScroll() {
  scrollPosition = window.scrollY;
  document.body.style.position = 'fixed';
  document.body.style.top = `-${scrollPosition}px`;
  document.body.style.width = '100%';
}

function unlockScroll() {
  document.body.style.position = '';
  document.body.style.top = '';
  document.body.style.width = '';
  window.scrollTo(0, scrollPosition);
}

Call lockScroll() when opening the dialog and unlockScroll() when closing it.

This technique stores the scroll position, fixes the body so it can’t scroll, then restores everything on close. It handles touch scrolling on iOS because the body literally cannot move.

Trade-off: You may see layout shift if the scrollbar disappears. Compensate by adding padding-right equal to the scrollbar width when locking.

CSS overscroll-behavior for Scroll Chaining

The overscroll-behavior property prevents scroll chaining—when scrolling inside one element causes a parent to scroll after reaching the boundary.

dialog {
  overscroll-behavior: contain;
}

dialog::backdrop {
  overscroll-behavior: contain;
}

Recent Chromium versions (late 2025+) improved this behavior, making it work even on non-scrollable containers. This helps with HTML dialog scroll locking in Chrome, but other browsers haven’t fully caught up yet.

Important: CSS overscroll-behavior modal solutions prevent scroll chaining, not scrolling itself. If the user scrolls directly on the backdrop or body, overscroll-behavior won’t stop it. Use this alongside other techniques, not as a replacement.

iOS Safari Quirks You Should Know

iOS Safari presents unique challenges for preventing background scrolling in modal implementations:

  • overflow: hidden on body doesn’t reliably block touch scrolling
  • The rubber-band overscroll effect can trigger background movement
  • Virtual keyboard appearance can cause unexpected scroll behavior

The fixed body technique remains the most reliable solution. Some developers also add touch-action: none to the backdrop element (not the dialog itself), though this can interfere with scrolling inside the dialog if applied too broadly.

Keep Your Dialog Scrollable

One critical detail: if your dialog content exceeds the viewport height, the dialog itself must scroll. Set appropriate max-height and overflow-y: auto on the dialog:

dialog {
  max-height: 90vh;
  overflow-y: auto;
}

Blocking all scrolling creates accessibility problems. Users need to reach all dialog content.

A Note on Popover API

The Popover API exists for lightweight overlays but isn’t a drop-in replacement for modal dialogs. Popovers don’t block background interaction the same way, and scroll locking behavior differs. For true modal experiences requiring scroll lock, stick with <dialog> and showModal().

Choosing Your Approach

For desktop-only applications, the CSS :has() approach with overflow: hidden often suffices. For cross-browser reliability—especially iOS Safari—use the fixed body technique with scroll position restoration. Layer overscroll-behavior: contain on top to prevent scroll chaining in supporting browsers.

Conclusion

No solution works perfectly everywhere. The fixed body technique offers the most reliable cross-browser scroll locking, particularly for iOS Safari. Combine it with overscroll-behavior: contain for additional protection against scroll chaining in Chromium browsers. Test on real devices, particularly iOS Safari, and accept that some edge cases may require compromise.

FAQs

iOS Safari handles touch scrolling differently than desktop browsers. The browser's touch event system can bypass overflow hidden on the body element, allowing background scroll even when the property is set. The fixed body technique works because it physically positions the body off-screen, making scrolling impossible rather than just hidden.

Yes, it can. When the scrollbar disappears, content may shift to fill that space. To prevent this, calculate the scrollbar width and add equivalent padding-right to the body when locking scroll. Remove the padding when unlocking. This maintains consistent layout throughout the modal interaction.

The Popover API serves a different purpose. It creates lightweight overlays without blocking background interaction or providing the same scroll locking capabilities. For true modal experiences where users must interact with the dialog before continuing, use the dialog element with showModal for proper focus trapping and inert behavior.

Set overscroll-behavior contain on the dialog to prevent scroll chaining to the background. Ensure your dialog has max-height and overflow-y auto so internal content scrolls properly. Avoid applying touch-action none to the entire dialog, as this blocks legitimate scrolling within the modal content area.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay