Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at RIA Unleashed (Nov. 2009) with: Lisa Heselton
Ben Nadel at RIA Unleashed (Nov. 2009) with: Lisa Heselton@kavka )

Using Position Absolute Inside A Scrolling Overflow Container

By Ben Nadel on
Tags: HTML / CSS

CAUTION: This is primarily a "note to self".

The other week, I tried to use absolute positioning inside a container that had "overflow: auto" enabled. And, somewhat to my surprise, the absolutely-positioned elements were rendered relative to the overflow "viewport," not to the "natural bounding box" of the content. This kind of threw me for a loop; and, it took me several days to come up with a solution. In retrospect, the solution is obvious. But, I had some weird mental block that was holding me back. All I had to do was wrap the content in non-overflow container and use said container as the anchor for positioning.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

To see what I mean, I've created a small demo in which I use the two approaches side-by-side. In the first approach, I'm using the overflow container as the position anchor. Then, in the second approach, I'm wrapping the content in an extra container in order to provide a non-overflow position anchor:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Using Position Absolute Inside A Scrolling Overflow Container
  • </title>
  •  
  • <style type="text/css">
  •  
  • div.viewport {
  • border: 5px solid #CCCCCC ;
  • box-sizing: border-box ;
  • height: 300px ;
  • overflow: auto ;
  • width: 500px ;
  • }
  •  
  • span.box {
  • background-color: #FF3366 ;
  • color: #FFFFFF ;
  • padding: 7px 15px 7px 15px ;
  • position: absolute ;
  • }
  • span.tl { left: 0px ; top: 0px ; }
  • span.tr { right: 0px ; top: 0px ; }
  • span.bl { bottom: 0px ; left: 0px ; }
  • span.br { bottom: 0px ; right: 0px ; }
  •  
  • </style>
  • </head>
  • <body>
  •  
  • <h1>
  • Using Position Absolute Inside A Scrolling Overflow Container
  • </h1>
  •  
  • <h2>
  • With A Positioned Overflow Container
  • </h2>
  •  
  • <style type="text/css">
  •  
  • div.viewport-a {
  • padding: 5px 15px 5px 15px ;
  • /*
  • Here, the overflow container is, itself, acting as the anchor for the
  • positioned elements contained within. This caused the absolutely
  • positioned elements to anchor to the actual viewport of the container.
  • */
  • position: relative ;
  • }
  •  
  • </style>
  •  
  • <div class="viewport viewport-a">
  •  
  • <span class="box tl">Top Left</span>
  • <span class="box tr">Top Right</span>
  • <span class="box bl">Bottom Left</span>
  • <span class="box br">Bottom Right</span>
  •  
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  •  
  • </div>
  •  
  • <!-- ---------------------------------------------------------------------------- -->
  • <!-- ---------------------------------------------------------------------------- -->
  •  
  • <h2>
  • With A Positioned Content Wrapper
  • </h2>
  •  
  • <style type="text/css">
  •  
  • div.viewport-b {
  • padding: 0px 0px 0px 0px ;
  • }
  •  
  • /*
  • Here, we're moving the position-anchoring off of the overflow container and
  • onto a content wrapper (contained within the overflow area). This allows the
  • absolutely positioned elements to anchor to the bounding box of the content,
  • not the viewport.
  • */
  • div.viewport-wrapper {
  • padding: 5px 15px 5px 15px ;
  • position: relative ;
  • }
  •  
  • </style>
  •  
  • <div class="viewport viewport-b">
  • <!--
  • By wrapping the content in a non-overflow container, we create an anchoring
  • box that is sized to the content, not to the viewport.
  • -->
  • <div class="viewport-wrapper">
  •  
  • <span class="box tl">Top Left</span>
  • <span class="box tr">Top Right</span>
  • <span class="box bl">Bottom Left</span>
  • <span class="box br">Bottom Right</span>
  •  
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  • <p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p><p>Yay, content!</p>
  •  
  • </div>
  • </div>
  •  
  • </body>
  • </html>

As you can see, the main difference between the two approach lies in where the "position: relative" style is applied. In the first approach, the relative position is applied to the "overflow" container; and, in the second approach, it's being applied to the content wrapper. Now, when we run this code, we get the following output:


 
 
 

 
 Using absolute position inside an overflow container. 
 
 
 

As you can see, in the first approach, the four corner boxes are positioned relative to the overflow viewport. And, in the second approach, the four corner boxes are positioned relative to the inner content wrapper, which creates a more "intuitive" bounding box for the content.

ASIDE: If you try the demo, you'll see that in the first approach the absolutely-positioned corner boxes scroll with the content. If you want the boxes to remain fixed in the corners, things get more complicated; especially in a context that may or may not present a scrollbar. When possible, you should avoid a user experience (UX) in which fixed elements overlap with scrollable content. Aside from nav-bars, it's a difficult experience to get right.

Looking back at this now, it seemed silly that this approach didn't occur to me immediately. But, at least I know now that I'll never forget it again.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

It's not that hard to create a more fixed position like experience but you will need to change your html.

Use the html and css from your second example except move the <span.box> elements out of the <div.viewport-wrapper> element so that they are direct children of the <div.viewport-b> element. Also, move position:relative; to the <div.viewport-b> element.

If you can't move them for whatever reason, using js to move them for you is probably the simplest solution. Otherwise you would have to do things like detect scroll and update a"top" value on the fly which is way more prone to bugs and poor performance.

Reply to this Comment

@Daniel,

The problem with moving the absolutely-positioned elements to the viewport is that you run the risk of overlapping with the scrollbar itself (unless you start to involve JavaScript detection in the matter). At least, that's how I see it working. If they are inside the overflow area, then the absolute elements will naturally move horizontally when a scrollbar is introduced or removed. However, if they are outside of the scrolling area, then you run the risk of "right:0px" overlapping with the scrollbar of the viewport.

Reply to this Comment

@Ben,

Hmmm... right, yeah that's a good point. That's a tricky puzzle. The only ways I can think of getting around that is either using an iframe or detecting if there is an overflow with JS and nudging the buttons over 20px if three is. The 20px nudge won't work properly on touch devices though since they don't have 20px scroll bars. `@ supports (pointer: fine)` might stop that issue.

Reply to this Comment

@Daniel,

Oh, I've not hear of "pointer:fine". I'm constantly shocked how many little CSS things there are that sneak in there unnoticed :D

For things that need to be fixed "near" things that are also scrolling, I've usually gone the route of just putting them in different containers entirely. So, treating them more like "toolbars" in a greater layout, as opposed to a fixed element inside a content container:

<div class="content"></div>
<div class="fixed-toolbar"></div>

... then, the toolbar just sits outside the scrollable area. You don't get the overlap, so you eliminate that problem. Except, it only works if you can get the entire size of the "toolbar" to be meaningful (ie, it wouldn't work just a button as it would leave a lot of empty space).

If anything, this is more of a "Design" problem than it is a "CSS" problem :P

Reply to this Comment

@Ben,

Sorry I meant @media (pointer: fine) { ... }, not @supports.
It detects if you are using a device with a highly accurate pointer like a mouse or a stylus pen. @media (pointer:coarse;) {...} is the opposite, detecting low accuracy pointers like touch screens and motion controls.

@media (hover: hover) {...} is also a thing that might work better detecting if the device supports hover states.

As for positioning, I get the feeling css-grid could come in handy here in some way. Grid lets you overlap components but still keep everything in flow and it makes it easy to line things up with one another.

Reply to this Comment

@Daniel,

I'm kind of fascinated with this "pointer" granularity. I wonder if one could use it to make buttons "bigger" on devices that are not finely-pointer'd. Or, would you just use the screen-dimensions for such a thing. I'll let that marinate in the back of my mind for a while.

Re: CSS Grid, I just recently started playing with it for the first time. Looks pretty cool!

Reply to this Comment

@Ben,

"I wonder if one could use it to make buttons "bigger" on devices that are not finely-pointer'd."

Lol, that is exactly why it was invented in the first place. So you can do stuff like that :)

"CSS Grid, I just recently started playing with it for the first time. Looks pretty cool!"

"Pretty cool" would be an understatement. I think css-grid is the most revolutionary thing to happen to css since css was invented! Layout isn't a nightmare any more. It's actually fun now :D

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.