Skip to main content
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Hal Helms
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Hal Helms

Using CSS Overscroll-Behavior To Prevent Scrolling Of Parent Containers From Within Overflow Containers

By
Published in Comments (11)

In the past, I've looked at how the scroll-wheel seems to randomly stop working in an overflow container. This phenomena is related to a browser feature called scroll chaining; and, it can be overcome if you prevent the wheel event's default behavior. Of course, tapping into the wheel and scroll events is not great for browser performance. Luckily, Derek Duncan stepped-in and told me about a CSS property called, overscroll-behavior. Supported in Chrome, Firefox, and Edge, this CSS property allows us to declaratively control what happens when an overflow container hits the edge of its scrollable content. This is a game changer!

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

If you have an overflow: auto element, the user can scroll the content contained within that element. However, when the user hits the top or the bottom of that content, the browser may start to scroll one of the ancestor elements, most commonly the body element. Once this "scroll chaining" occurs, subsequent use of the mouse-wheel may not be applied to the overflow: auto element; instead, the expression of the scrolling may continue to manifest in the ancestor element.

More often than not, this leads to an unexpected and undesired user experience (UX). Really, what we want to happen is to have the scrolling behavior always contained within the overflow: auto element. To do this (in Chrome, Firefox, and Edge), we can add the CSS property overscroll-behavior: contain to the overflow: auto element. This will prevent the "scroll chaining" behavior, which will, in turn, keep the mouse-wheel active within the target element.

To see this in action, I have two overflow: auto elements laid-over a scrollable body element. The container on the left has no modifying CSS properties while the container on the right has the CSS property overscroll-behavior: contain:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>
		Using CSS Overscroll-Behavior To Prevent Scrolling Of Parent Containers From Within Overflow Containers
	</title>

	<style type="text/css">

		html {
			box-sizing: border-box ;
		}

		*, *:before, *:after {
			box-sizing: inherit ;
		}

		.layout {
			background-color: #ffffff ;
			border: 5px solid #cccccc ;
			height: 500px ;
			margin: -250px 0px 0px 0px ;
			position: fixed ;
			top: 50% ;
			width: 300px ;
		}

		.layout--a {
			right: 51% ;
		}

		.layout--b {
			left: 51% ;
		}

		.layout__top-panel {
			bottom: 100px ;
			left: 0px ;
			overflow: auto ; /* The panel becomes SCROLLABLE due to content overflow. */
			position: absolute ;
			right: 0px ;
			top: 0px ;
		}

		.layout--b .layout__top-panel {
			overscroll-behavior: contain ; /* Prevent SCROLL-CHAINING to parent elements. */
		}

		.layout__bottom-panel {
			border-top: 1px solid #cccccc ;
			bottom: 0px ;
			font-weight: bold ;
			height: 100px ;
			left: 0px ;
			padding: 20px 20px 20px 20px ;
			position: absolute ;
			right: 0px ;
		}

		.content p {
			margin: 0px 0px 0px 0px ;
			padding: 25px 20px 22px 20px ;
		}

		.content p:nth-child( even ) {
			background-color: #f0f0f0 ;
		}

	</style>
</head>
<body>

	<h1>
		Using CSS Overscroll-Behavior To Prevent Scrolling Of Parent Containers From Within Overflow Containers
	</h1>

	<!-- BEGIN: Layout-A. -->
	<section class="layout layout--a">
		<div class="layout__top-panel">

			<div class="content">
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
			</div>

		</div>
		<div class="layout__bottom-panel">

			No Behavior Modification

		</div>
	</section>
	<!-- END: Layout-A. -->

	<!-- BEGIN: Layout-B. -->
	<section class="layout layout--b">
		<div class="layout__top-panel">

			<div class="content">
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
				<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
			</div>

		</div>
		<div class="layout__bottom-panel">

			Using Overscroll-Behavior

		</div>
	</section>
	<!-- END: Layout-B. -->


	<!-- BODY content. -->
	<div class="content">
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
		<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
	</div>

</body>
</html>

Notice that the only difference is that the overflow: auto container on the right has the overscroll-behavior directive. Now, if we run this HTML and CSS code in the Chrome browser, we get the following output:

Overflow-behavior: contain prevents scroll chaining in overflow: auto element using CSS.

As you can see, when users scrolls to a local maxima within the scrollable container on the left, subsequent use of the scroll wheel causes the body element to scroll. This is "scroll chaining" in action. However, with the scrollable container on the right, which uses overscroll-behavior: contain, the scrolling is contained within the element - no "scroll chaining" occurs and the mouse wheel never affects the body scroll.

This is awesome! I will literally be shipping this to production today. The overscroll-behavior CSS property is perfect for things like rich HTML dropdown menus, modal windows, and fly-out panels. A true game-changer for creating a more positive user experience (UX) in my Angular, Single-Page Applications (SPA). Much thanks to Derek Duncan for learning me some new CSS hawtness!

Want to use code from this post? Check out the license.

Reader Comments

247 Comments

Awesome! I can use this TODAY! Love it when something like this falls right into my lap, solving a problem I didn't realize had a solution!

1 Comments

This is great! Unfortunately does not work on Safari, which is too bad since I need it for iOS :(

Will circle back in the future if I ever run into the need again though.

15,902 Comments

@Ted,

That said, the beauty of CSS like this is that you can use it a "progressive enhancement" feature. Meaning, you can put it in, and then users who have browsers that support it will get a slightly better experience; and, users who have browsers that do not support it, will just get the "usual" experience. So, it's basically just a value-add.

I would also mention, though, that mobile Safari uses a very different scrolling behavior for reasons that relate to small-screen usability. It might not be worth it to try an override it.

15,902 Comments

@Dmitriy,

Ha ha, glad this was helpful. I use this CSS property all the time these days. It's a wonderful progressive enhancement.

1 Comments

Very cool. Is there a way to scroll the outer box first? Also allow me to specify scroll direction, something like "scroll-down-outer-first"

I've got a feature request recently, asking to scroll the outer container first if scroll direction is downward.

Never done this before, in the end, I used onwhell event to pull this off: https://realrz.com/pub/video/scroll_outer_first.mov

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel