How to prevent page scrolling under a modal dialog
Short answer
While a <dialog> is open, turn off page scrolling with a :has() rule — no JavaScript is needed for that:
<style>
html:has(dialog[open]) { overflow: hidden; }
</style>
<button onclick="box.showModal()">Open the dialog</button>
<dialog id="box">
<p>The page behind does not scroll now.</p>
<button onclick="box.close()">Close</button>
</dialog>
<p style="height: 600px;">Long page content…</p>One line of script opens and closes the dialog, and CSS locks the scrolling: as soon as <dialog> gets the open attribute, the root element gets overflow: hidden.
Scrolling inside the dialog
If the dialog has its own long list, scrolling past its end makes the browser scroll the page underneath. overscroll-behavior stops that:
.modal-body { max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; }The page jump
When the scrollbar disappears, the page gets wider and the content shifts right. scrollbar-gutter: stable on html reserves room for the bar in advance, and nothing jumps.
Common mistake
overflow: hidden is put only on body, while it is actually the root html element that scrolls — especially if it has a height or its own overflow somewhere. The page keeps scrolling. Set the rule on html, as in the example — or on both elements.
Functions used in this recipe
- overflow — What to do with content that does not fit into the element.
- :has() — Matches an element that contains something matching the selector in brackets — the "parent selector".
- <dialog> — A dialog box; a modal one is opened with `showModal()` and blocks the rest of the page.
- <body> — The visible part of the page: everything the user sees.