views:

519

answers:

3

I want to achieve a simple "frame" layout with fixed header, fixed left navigation area, and a main content area that fills 100% of the remainder of the viewport with scrollbars if necessary. My best attempt is below - but when I add enough content to the main div to force scrolling, I see that the scrollbar extends below the bottom of the viewport.

What am I doing wrong? Or what is IE6 doing wrong and how can I fix it?

NB please don't recommend using a more recent browser - I'd love to but can't.

Update 1 (for Matti and other purists!) - I have to live within real-world constraints of developing a web application for the head office of a group which needs to be accessed by users in over 100 subsidiaries, not all of which have upgraded to a modern browser. And some subsidiaries would love to use browser incompatibility as an excuse not to use the application imposed by head office!

Update 2

I'm an application developer, not a web designer, as is probably obvious. To date I have been using a DOCTYPE HTML 4.0 Transitional which I believe forces quirks mode in all IE version. However I recently tried adding the AjaxControlToolkit ModalPopupExtender, and this messes up my layout when the popup is displayed. Google suggested I need to use XHTML 1.0 to fix this (AjaxControlToolkit doesn't support quirks mode), and in fact I'm quite happy to move to cleaner CSS-based layout, but I do need my basic frame layout to work in IE6.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title></title>
    <style type="text/css">
    html, body
    {
        height:100%;
        margin:0;
        padding:0;
        overflow:hidden;
    }
    div
    {
        border:0;
        margin:0;
        padding:0;
    }
    div#top
    {
        background-color:#dddddd;
        height:100px;
    }
    div#left
    {
        background-color:#dddddd;
        float:left;
        width:120px;
        height:100%;
        overflow:hidden;
    }
    div#main
    {
        height:100%;
        overflow:auto;
    }
    </style>    
</head>
<body>
    <div id="top">Title</div>
    <div id="left">LeftNav</div>
    <div id="main">
    Content
    <p>
    Lorem ipsum ...
    </p>
    ... repeated several times to force vertical scroll...
        <table><tr>
        <td>ColumnContent</td>
        ... td repeated several times to force horizontal scroll...
        <td>ColumnContent</td>
        </tr></table>
    </div>
</body>
</html>
+1  A: 

In my previous answer, I was absolutely wrong (no pun intended), as you can't specify both top and bottom in IE6, neither both left and right. Moreover, you don't know the exact width and height of the content div, nor do you know them as a percentage of the viewport.

When I solved this, I put IE into quirks mode, to get the border-box box model (see also W3C spec). To use the same CSS rules for more standards compliant browser, you can use the box-sizing property (and variants). After doing this, the borders get inside the contents and you can push your contents down and to the right by specifying a border (width and style):

<!-- put IE in quirks mode -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
  <title>IE6 'frames'</title>
  <style type="text/css">
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      -moz-box-sizing: border-box;
      -khtml-box-sizing: border-box;
      -webkit-box-sizing: border-box;
    }

    html, body {
      height: 100%;
      overflow: hidden;
    }

    #top {
      position: absolute;
      top: 0;
      width: 100%;
      background-color: #ddd;
      height: 100px;
      z-index: 2;
    }

    #left {
      position: absolute;
      left: 0;
      border-top: 100px solid;  /* move everything below #top */
      background-color: #bbb;
      width: 120px;
      height: 100%;
      overflow: hidden;
      z-index: 1;
    }

    #main {
      position: absolute;
      border-top: 100px solid;
      border-left: 120px solid;
      width: 100%;
      height: 100%;
      overflow: auto;
    }
  </style>    
</head>
<body>
  <div id="top">Title</div>
  <div id="left">LeftNav</div>
  <div id="main">
    <p>
      Lorem ipsum ...<br />
      <!-- just copy above line many times -->
    </p>
  </div>
</body>
</html>

UPDATE: In IE >= 7 and more standards compliant browsers you can use position: fixed together with both top and bottom (et al.) rules. There is a way to get this frame-like appearance in IE6 in Standards Mode (or rather, Almost Standards Mode) using CSS expressions. This way, you can let the JScript engine calculate the correct values of width and height (added between conditional comments).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
  <title>'Frames' using &lt;div&gt;s</title>
  <style type="text/css">
    * {
      margin: 0;
      padding: 0;
    }

    html, body {
      height: 100%;
      overflow: hidden;
    }

    #top, #left, #main {
      position: fixed;
      overflow: hidden;
    }

    #top {
      top: 0;
      width: 100%;
      background-color: #ddd;
      height: 100px;
    }

    #left {
      left: 0;
      top: 100px;  /* move everything below #top */
      bottom: 0;
      background-color: #bbb;
      width: 120px;
    }

    #main {
      top: 100px;
      left: 120px;
      bottom: 0;
      right: 0;
      overflow: auto;
    }
  </style>
  <!--[if IE 6]>
  <style>
    #top, #left, #main {
      position: absolute;
    }

    #left {
      height: expression((m=document.documentElement.clientHeight-100)+'px');
    }

    #main {
      height: expression((m=document.documentElement.clientHeight-100)+'px');
      width: expression((m=document.documentElement.clientWidth-120)+'px');
    }
  </style>
  <![endif]-->
</head>
<body>
  <div id="top">Title<br /></div>
  <div id="left">LeftNav<br /></div>
  <div id="main">
    <p>
        Lorem ipsum ...<br />
        <!-- just copy above line many times -->
    </p>
  </div>
</body>
</html>

That said, I don't recommend this method. It will slow down the browsing experience of the already not too fast IE6 noticeably, as these expressions are evaluated many times.

Just one more sidenote: I suppose you'll use external style sheets (and scripts) in the end, but if you want to embed those inside an XHTML document, use “CDATA markers and comments appropriate for the script or style language used”, as David Dorward recommends.

Marcel Korpel
+1 works in quirks mode. However I want to avoid using quirks mode - see update to my question. Is this possible?
Joe
This is the only answer so far that gives exactly the rendering I want - but only works in quirks mode. I'm struggling to get the same effect in standards mode even when using IE8 - do you have a version of this that works in IE8 in standards mode, even if it doesn't render properly in IE6?
Joe
Excellent, your help is much appreciated. Assuming it's impossible in IE6 in standard mode, I'd like to get the best possible approximation in IE6 standards mode. For example, if necessary I guess I'd have to accept that the whole body scrolls in IE6 if the content is too wide or high. If you can show me how to do this I'd be grateful, but I'll also have a go myself using your second version as a starting point.
Joe
I really appreciate this excellent answer, that explains things clearly for a CSS beginner like myself, and provides links for more advanced topics. I'll try your "EDIT 2" IE6 solution tomorrow: in my situation (in-house LOB application) it's acceptable that users who haven't upgraded their browser have inferior performance.
Joe
... your link "Almost Standards Mode" is both interesting and a bit scary. It seems to be suggesting that XHTML doctypes shouldn't be used! Do you have a recommendation of what Doctype to use in my situation (a) in-house LOB application, (b) cross-browser and standards-compliant as far as possible, (c) IE6 support needed (some degradation of user experience acceptable), (d) I'm not a purist. For example I see that a VS2010 ASP.NET app has XHTML 1.0 Strict by default.
Joe
Just tested this in IE6 and it doesn't work :( The #main div is rendered below the #left div. And if I make the #left div 100% high with a CSS expression, the #main div is rendered below the bottom of the viewport. I'll play around with this, but if you have a solution I'd appreciate it. I'm amazed at how hard this is!
Joe
I've got a version I can use as a fallback in IE6, where the whole page scrolls rather than just the content area - i.e. the title and left navigation are scrolled off the page. I guess I'll have to live with this unless you can find something better. To do so I added the following CSS conditionally for IE6: (a) #left { float:left; height:expression(...); } to get the left nav bar on the left of the content and 100% high (b) #main { position:absolute; } to get main to position to the right of the left nav bar, and (c) html { overflow:auto; } so that the whole page scrolls.
Joe
@Joe: Regarding the Doctype: if you have to use XHTML for some reason or another (3rd-party library), just use it. As Ian Hickson [suggests](http://hixie.ch/advocacy/xhtml), you shouldn't send XHTML as text/html, but you'll have to if you want to support IE (6/7/8). If your not a purist, just stick with it.
Marcel Korpel
@Marcel, thanks again for spending time on this. In IE6 your second ("EDIT") version #left is only one line high, and #main is underneath it. Setting the height on #main with an expression (your EDIT2) doesn't change this, #main still renders below #left. Setting the height on #left using an expression makes it fill 100% of the viewport height correctly, but #main is still below #left and therefore off the bottom of the viewport.
Joe
@Joe: I just updated my answer using CSS expressions. IE6 doesn't know `position: fixed` and the thing I forgot was that in those cases it falls back to `position: static`, so you have to explicitly position those divs absolutely.
Marcel Korpel
@Marcel, thanks once again. StackOverflow should really provide a way to identify really excellent and useful answers like this one. I'm on holiday for a long weekend now so will test this in IE6 next Wednesday.
Joe
@Joe: :) You're welcome! Have a nice holiday!
Marcel Korpel
@Marcel, I had a problem with this solution, in that the AjaxControlToolkit ModalPopupExtender does not work properly when used inside a relative or absolute div. However I've managed to work out a variant of the above where #main is position:static for IE6 which seems to work - see http://stackoverflow.com/questions/3047181/modalpopupextender-doesnt-work-on-ie6-frame-layout/3092254#3092254
Joe
+1  A: 

This html should give you a bit of help but your probably going to have to fiddle with it to get it to work perfectly in ie6 (sorry I can't test in ie6 right now). The problem is happening because there is actually a scroll bar appearing on the html and body where you have overflow:hidden specified. You can take that off and set overflow to auto to actually see it occurring.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <style type="text/css">
        html
        {
            height:100%;
        }
        body
        {
            height:100%;
            margin:0px;
            padding:0px;
            border-left:0px;
            border-right:0px;
            overflow:hidden;
        }
        #header
        {
            top:0px;
            width:100%;
            height:100px;
             background-color:#dddddd;
            position:absolute;
        }
        #content
        {
            top:100px;
            bottom:0px;
            width:100%;
            overflow:auto;
            position:absolute;
        }

        #wrapper
        {
        padding-left:125px;
        }

        div#left
    {
        background-color:#dddddd;
        float:left;
        width:120px;
        height:100%;
        overflow:hidden;
    }
    </style>
</head>
<body>
    <div id="header"></div>
    <div id="left"></div>
    <div id="content">
    <div id="wrapper"> 
    <p>content</p>
    <p>content</p>
    </div>
</div>
</body>
</html>
Ben
"there is actually a scroll bar appearing on the html and body" - I can see that when I use overflow:auto. Is there a way I can eliminate it?
Joe
This doesn't work in IE8 - the content area extends beyond the right side of the viewport.
Joe
... Further to the above comment: I get different results with IE8 on two different machines - I suspect it's due to the way IE8 is configured. On my home machine, vertical scrolling works fine as long as the content is not too wide. But I still have problems with horizontal scrolling with wide content (e.g. a wide table). At work, it doesn't work at all - I'll look more closely at the browser configuration tomorrow.
Joe
... and just to confirm for anyone who's interested that on IE8 this gives different results depending on whether IE8 is showing Document Mode "IE7 Standards" or "IE8 Standards". But in neither case does horizontal scrolling work for wide content.
Joe
+1  A: 

http://www.cssplay.co.uk/layouts/frame.html

reisio
Looks very like what I want - I'll have a play with it tomorrow and see if I can work out what the salient bits are I need to extract.
Joe
+1 for the nice bit of virtuoso CSS, but ultimately it doesn't do what I need. The content area has fixed width columns: in my case I have an application which may have wide content (e.g. a Table with many columns), and I need horizontal scrolling in this case.
Joe