Layout Beginner

Preventing scroll chaining without JavaScript

When a scrollable element inside a modal reached its end, scroll would chain to the page behind it. The fix was a JS wheel event listener calling e.preventDefault(). overscroll-behavior: contain stops that natively.

4 lines
1.modal-content {
2  overflow-y: auto;
3  overscroll-behavior: contain;
4}
Old 8 lines
1.modal-content { overflow-y: auto; }
2// JS: prevent scroll chaining on wheel and touch
3modal.addEventListener('wheel', (e) => {
4  e.preventDefault();
5}, { passive: false });
6// also needed for touch: touchmove listener
Widely available Since 2022 96% global usage

This feature is well established and works across many devices and browser versions. It has been available across browsers since 2022.

63+
59+
16+
18+
scroll inside the box β€” the page does not scroll
Scrollable panel (overscroll-behavior: contain)
Row 1 β€” scroll past the end!
Row 2 β€” scroll past the end!
Row 3 β€” scroll past the end!
Row 4 β€” scroll past the end!
Row 5 β€” scroll past the end!
Row 6 β€” scroll past the end!
Row 7 β€” scroll past the end!
Row 8 β€” scroll past the end!
Row 9 β€” scroll past the end!
Row 10 β€” scroll past the end!
Row 11 β€” scroll past the end!
Row 12 β€” scroll past the end!
Scroll ends at the panel boundary β€” no JS needed
Kinsta

Your first month is free

Managed WordPress hosting for faster sites.

Learn more
⚑

No JavaScript

No wheel or touchmove event listener, no preventDefault, no passive flag concerns.

✦

Works on touch too

Handles touch scroll chaining on mobile without the complexity of non-passive event listeners.

∞

Pull-to-refresh too

overscroll-behavior: none also prevents pull-to-refresh on Chrome Android for full-screen app-like layouts.

Lines Saved
8 β†’ 4
No wheel event handler
Old Approach
e.preventDefault()
wheel and touchmove event listeners
Modern Approach
overscroll-behavior
CSS scroll containment

How it works

Scrollable panels inside modals or dropdowns would chain scroll to the page once the panel reached its end. Stopping this required non-passive wheel and touchmove event listeners calling e.preventDefault(), which also blocked the browser's scroll optimizations and required separate handling for touch.

overscroll-behavior: contain stops scroll propagation at the element's boundary. The inner element scrolls normally, but the chain ends there. overscroll-behavior: none additionally prevents the bounce effect and pull-to-refresh on supported platforms.

New CSS drops.

Join 600+ readers who've survived clearfix hacks.

ESC