Conflicting Absolute Positions
Issue № 241

Conflicting Absolute Positions

On two separate occasions this month, I’ve been required to produce a layout in which a fixed-width scrolling side “pane” and flexible scrolling main “pane” must resize to fill all available space in a browser window.

Article Continues Below

As a CSS purist, I’ve always tried to avoid such dynamic layouts, but sometimes they’re required. When they are, I like to have a good old grumble about the fact that I’ve resorted to using JavaScript for my layouts.

The most advanced way of achieving such a layout is to use a JavaScript toolkit such as DOJO—but for what I was trying to achieve, even DOJO felt too bloated and seemed liable to create further complications.

We can, of course, achieve these layout goals by using JavaScript to resize the divs every time the page is loaded or resized. Unfortunately—among other headaches—that’s made more complicated by the choices between window.innerHeight and document.documentElement.clientHeight and document.body.clientHeight, and the need for cross-browser event listeners.

It seems that using JavaScript is an attainable but inelegant solution for this layout. What I really want is a lightweight, easy-to-understand, pure CSS template…

The problem with percentages#section2

We often use elements that have dynamic widths and heights by defining those attributes as percentages. But there’s a problem with percentages: they don’t play well with others. Despite everyone’s best attempts, you just can’t mix up pixels and percentages (although I’ve read rumors that it’s in the cards).

While we can create relatively effective layouts using just percentages,  we can’t then have a fixed-width side panel or a fixed-height header. So percentages just aren’t going to do the job in our layout.

The nature of divs#section3

Adopting a tried and tested philosophical technique, I went back to the basic assumptions to try and find something else I’d missed. I realized that I thought the following statements about divs were all true:

In all browsers#section4

  1. A div is rectangular.
  2. Only one corner of a div can be absolutely positioned on a page.
  3. The location of the diagonally opposing corner must be determined by the width and height of the div.
  4. If the width and height are dynamic, they must be determined using JavaScript.

It struck me that assumption no. 2—that only one corner of a div can be absolutely positioned on a page—should be very easy to either confirm or deny. What happens if we absolutely position a div by defining its top, left, bottom, and right properties, all at the same time? After all, although I think of it as being a “conflict,” it’s actually perfectly valid CSS.

Assigning “conflicting” absolute positions#section5

I had assumed that if you assigned top, left, bottom, and right properties that most browsers would simply ignore two of those properties.

It seemed like a fair assumption at the time. I expected some variation between the browsers, but I also expected all of them to ignore two of the four positions.

I was utterly wrong about that. What actually happens is something rather magical. In every browser I tested, with the exception of IE5 and IE6, all four rules are obeyed. The result is that the div is effectively “stretched” to fill the viewport.

DIV example
fig. 1, DIV size is computed in most browsers when absolute positioning is specified.

A bit of research revealed that I’m certainly not the first person to discover this.

“In browsers that support CSS you can specify all four edges and let the browser compute the width and the height. Unfortunately this doesn’t work in Internet Explorer…”

Autistic Cuckoo

“Technically you can use both a right and a left property and leave the width as auto. But unfortunately IE does not support this…”

css-discuss

In general, this little bit of CSS trickery seems to have been discarded due to its incompatibility with IE5 and IE6, and as a result has remained largely unnoticed, although it’s nice to see that IE7 now supports these “conflicting” absolute positions.

Incompatibility doesn’t make the discovery useless. Our original statements may still apply to IE5 and IE6, but we now have a different set of statements for all other browsers.

In all browsers except for IE5 and IE6#section6

  1. A div is rectangular.
  2. All four corners of a div can be absolutely positioned on a page.
  3. If the location of diagonally opposing corners has been determined the width and height is implied.

An alternative solution for IE#section7

What does this mean? It means our assumption that only one corner of a div can be absolutely positioned on a page creates a problem specifically for IE5 and IE6. As it turns out Internet Explorer actually offers us its own alternative. Earlier on, I said that you can’t mix pixels and percentages, but that wasn’t strictly true: you can in Internet Explorer.

Using the power of dynamic properties, it is now possible to declare property values not only as constants, but also as formulas.

Dynamic properties are undeniably powerful, but as they’re only supported in IE, they tend to be of little real use.

But, once again, incompatibility isn’t a reason for discarding this little trick. Being able to determine the width and height of our divs as a formula means we can specify “the width of the page minus 40px.” As long as we can do that in IE5 and IE6, we can modify our original assumption #4 just a little bit and settle on our final set of statements.

In IE5 and IE6#section8

  1. A div is rectangular.
  2. Only one corner of a div can be absolutely positioned on a page.
  3. The location of the diagonally opposing corner must be determined by the width and height of the div.
  4. The width and height can be determined using dynamic properties.

In all other browsers:#section9

  1. A div is rectangular.
  2. All four corners of a div can be absolutely positioned on a page.
  3. If the location of diagonally opposing corners has been determined, the width and height is implied.

Ah! Now that’s much more like it. As long as all of the above statements are true, we really should be able to put our entire template together using (almost) pure CSS.

Putting it all together#section10

Our wonderfully simple HTML looks like this:

<body>
  <div id="header">
    <p>Our header</p>
  </div>
  <div id="side">
    <p>Our side panel</p>
  </div>
  <div id="right">
    <p>Our main panel</p>
  </div>
</body>

In modern browsers#section11

Our CSS for all modern browsers is now strikingly simple.

  1. We specify the width and height of the body as 100%. (This is actually only needed for our Internet Explorer solution, but there’s absolutely no harm in including it in our main CSS.)
  2. We hide the overflow in the body and html because we never want to see those scroll bars again.
  3. We set the overflow to “auto” for the left and right panels, and hide it in the header.
  4. The header has a width of 100% and a constant height of 80px.
  5. For the side panel we specify the top (header height + padding), left (padding), and bottom (padding) positions. Then we give it a constant width of 200px.
  6. For the right panel we specify the top (header height + padding), left (padding + side panel width padding), right (padding) and bottom (padding) positions.

All of that is very easily translated into the following CSS:

<style type=”text/css”>html {
  overflow: hidden;
}body {
  overflow: hidden;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
}#header {
  padding: 0;
  margin: 0;
  position: absolute;
  top: 0px;    
  left: 0px;
  width: 100%;
  height: 80px;
  overflow: hidden;
}#side {    
  padding: 0;
  margin: 0;
  position: absolute;    
  top: 100px;
  left: 20px;
  bottom: 20px;
  overflow: auto;    
  width: 200px;    
}#main {
  padding: 0;
  margin: 0;
  position: absolute;    
  top: 100px;
  left: 240px;
  right: 20px;
  bottom: 20px;
  overflow: auto;
}</style>

Creating the exception for IE5 and IE6#section12

In IE5 and IE6 the bottom and right attributes of the main and left panels are just ignored.

This means that the top left corner is still pinned in place for each of our divs, and we just need to define our widths and heights.

We want the height of both the main panel and the side panel to be 100% of the height of the page minus the header height and the top and bottom padding (100%-80px-20px-20px).

We want the width of the main panel to be 100% of the width of the page minus the width of the side panel, the left padding, the right padding, and the gutter padding (100%-200px-20px-20px-20px). The width of the side panel is a constant, and has already been defined, so nothing needs adding here.

By using a conditional comment we can include these expressions for IE5 and IE6. (Line wraps marked » —Ed.)

<!--[if lt IE 7]>

<style type="text/css">

#side {
  height:expression(document.body.clientHeight-120); »
 /* 80+20+20=120 */  
}

#main {
  height:expression(document.body.clientHeight-120); »
/* 80+20+20=120 */  
  width:expression(document.body.clientWidth-260); »
/* 200+20+20+20=260 */    
}

</style>

<![endif]-->

Don’t forget: we specifically had to set the height and width of the body to 100% for this to work, but we didn’t need to hide that from other browsers, so it’s included in the main style sheet.

Beautiful#section13

And there we have the finished layout.

Okay, those dynamic expressions aren’t valid, but they are at least hidden from the browsers that don’t need them. Although they’re presented as CSS those dynamic expressions are in truth JavaScript, and as such they won’t work in IE5 and IE6 if JavaScript is turned off.

But then, none of the alternative solutions would work in that situation either.

{Although this technique was developed independently, an article that suggested many of the same methods was published in 2004 by Alex Robinson. —Ed.}

Known issues#section14

There’s a small and annoying bug in Opera 8. Although the side div resizes correctly when the page first loads, it doesn’t dynamically resize when the window size is changed.

This seems to be because we’ve given it a constant width, and I have, so far, been unable to find a way around this issue. Happily, it’s fixed in Opera 9, and it isn’t a particularly critical bug to begin with.

77 Reader Comments

  1. @John Campell, others —

    I thought I had a way to fix the 100% & float problems inside the absolutely positioned elements in IE — add position: relative; to the .container divs.

    However, that didn’t do it. But I was unable to find a problem with floats in IE, anyway, just with width: 100%.

  2. I didn’t realise that so many people were vague on positioning so its good that your article brings this to attention.

    As a matter of interest you might like to know that the original example can easily be done without using expressions (and in standards mode)and I have converted one of my very (very old) demos to look like the authors.

    http://www.pmob.co.uk/temp/2scroll2.htm

    or an even older similar example.
    http://www.pmob.co.uk/temp/3scrolling2.html

    Instead of using expressions to subtract pixel heights from 100% you simply apply top and bottom padding to the html element and then IE interprets 100% height of the body from inside the padding thus giving you your gaps at the top and bottom of the page.

    Of course it only works in 100% height scenarios and so is of limited use. I often find that these types of layouts (along with forced fixed positioning examples) only work well in certain situations but not for everything as there are quite often scrollbar issues.

    I also notice this quote early in the article which is a little misleading:
    [quote]
    “Technically you can use both a right and a left property and leave the width as auto. But unfortunately IE does not support this”¦”?—css-discuss
    [/quote]

    That isn’t quite true and IE does support the properties left and right at the same time and you would soon see this if you tried it out.

    #main {
    position: absolute;
    top: 100px;
    left: 240px;
    right: 120px;
    background:red
    }

    The misconception arises because the background appears to only enclose the content but if there is enough content then it stretches correctly to the positions defined. In fact this can be cured by giving the inner content “haslayout” and then it works as it should.

    Its only top and bottom positions that IE doesn’t understand when used together.

  3. #44: the bug you describe does surely exist. I can confirm both the background from the preceding page and pixel distortion on window resize.

    I am really interested in a solution to this problem. I would like to adopt this approach, but my company officially supports Safari 1.3.2 :=(

  4. Sorry for the huge text in 55, it looked fine in the preview..

    Scrolling does not work in Safari 1.3.2 – while the scroll bar is moving, the content is fixed, which means that you can never see any content below.

  5. Has anyone tried using this technique to create a 2- or 3-column layout that goes beyond the bottom of the window and where both or all of the columns stretch to the same height as a “height-defining” column? I believe I’ve figured out how to do this in all browsers (no, I didn’t use faux columns or funky paddings or margins). I was wondering, Rob, if you’ve seen this before and if not, if you’d be interested in hearing my method. I read an article that said this would be the “holy grail” of CSS web design.

  6. I use this technique or variants very heavily, thanks to the frames without frames article published (and cited) a while back.

    Be forewarned: VERY rarely, and usually inconsistently, Expressions can cause the IE browser to hang in an infinite loop. What appears to happen is the dimension gets calculated which causes the expression to trigger again, ad infinitum. There are some discussions of this, but I don’t have the links.

    For me, it was the width being adjusted. In the end I had to pull it from the CSS,and put it in a javascript embedded in the footer of the page. I didn’t like doing so, because CSS is where it should be, but I had to. So people should be aware of this. While I was at it, I fixed the opera issues in JS too (window.opera is a good way to object sniff for opera)

  7. Hi, i was looking for the same layout for some time ago, found the same solution with the absolute positioning of each corner, and ran in to the same problems in IE6 og earlier versions.. After a bid of work i actualy manged to find af solution that works alle the way down to IE5 in pure CSS without the need for Javascript. – Will put it online and post a link if anyone should be interessetet 🙂

  8. Does anyone know of a way that I could achieve a similar effect using the “em” size unit?
    I would like my header to be able to fully encapsulate its text, regardless of the text size the user has chosen.

  9. I noticed that plenty have reported the problem with absolutely positions containers that incorrectly size containing DIVs with 100% widths.

    I found that it no scrollbar appears in IE7/Win, FF2/Win, O9/Win, NN9/Win, S3/Win.

    The problem I’m experiencing is that I cannot set the height of DIVs to 100% in IE7/Win. It works in all the above mentioned browsers.

    Have anyone got a solutions for this problem?

  10. Thanks for taking the time to write this great, clear article.

    I’ve done this layout using pure CSS, but using individual BORDERS (as an earlier commenter noted) as if they were margins or padding. The neat trick he didn’t mention: you can set BORDER-COLOR to TRANSPARENT so no need to match background colors or designs. 100% fills the remaining calculated width. You might have to keep an eye on your z-index layering when constructing to prevent mouse events from being captured in the wrong DIV.

  11. Whilst your CSS is clean and effective I thought I should point out a few difficulties that it exposes.
    Firefox has a well known and long established print bug exposed by the use of absolute positioning – content is curtailed, usually to a single page.
    Firefox mousewheel navigation (the autoscroll feature) is also broken.
    Neither of these may be considered show stoppers for some but developers might like to be aware of these issues.

  12. Whilst the solution is neat and taught me things I didn’t know, I do have a worry about those users who can’t use a mouse for some reason.

    As exampled it seems impossible to get the DIVs to scroll down using keystrokes. It seems to me that a sighted user who cannot use a mouse would be unable to access the text at the bottom of both the DIVs.

    In a similar layout with frames tabbing would give focus to each frame in turn, and then the up and down arrow keys would scroll the individual frames.

    Does anyone have any suggestions?

  13. I really enjoyed the article.

    One issue I’m having in IE7 is getting a nested DIV to respect the calculated height of the parent div. In Firefox, setting the height property to 100% yields the desired result; that is, a div that consumes the entire height of the underlying element. In IE7, the height is ignored, presumably because the element’s height is calculated rather than specified in CSS.

    If someone has a workaround, I’d be grateful for tips.

  14. The layout I’m stuck with has three fixed width columns. For over two years I have tried different ways to get IE6 (may it someday RIP) to extend the background of the right and left columns all the way to the bottom of the center column (where the varying height main content is). I don’t even have (or need or want) scroll bars.

    Maybe there’s an expression-free method to accomplish this somewhere out there, but I haven’t been able to find it. I’m just glad this approach works perfectly for my purpose.

    Thanks for taking the time to track down and explain a classic head-scratcher to this non-expert.

  15. Thanks, even if we’re seeing IE6 fade away, it is still very much a reality with Asian users.
    I have been looking for a cross-browser option of pinning a div to the viewport’s height, preferably without the use of jquery or equivalent.
    This is brilliant.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career