views:

14997

answers:

9

This has been driving me crazy for a couple of days now, but in reality it's a problem that I've hit off and on for the last few years: With HTML/CSS how can I make an element that has a width and/or height that is 100% of it's parent element and still has proper padding or margins?

By "proper" I mean that if my parent element is 200px tall and I specify 100% height with 5px padding I would expect that I should get a 190px high element with 5px "border" on all sides, nicely centered in the parent element.

Now, I know that that's not how the standard box model specifies it should work (although I'd like to know why, exactly...), so the obvious answer doesn't work:

#myDiv {
    width: 100%
    height: 100%;
    padding: 5px;
}

But it would seem to me that there must be SOME way of reliably producing this effect for a parent of arbitrary size. Does anyone know of a way of accomplishing this (seemingly simple) task?

Oh, and for the record I'm not terribly interested in IE compatibility so that should (hopefully) make things a bit easier.

EDIT: Since an example was asked for, here's the simplest one I can think of:

<html style="height: 100%">
    <body style="height: 100%">
        <div style="background-color: black; height: 100%; padding: 25px"></div>
    </body>
</html>

The challenge is then to get the black box to show up with a 25 pixel padding on all edges without the page growing big enough to require scrollbars.

+1  A: 

I've found these two solutions to be the most reliable:

http://www.xs4all.nl/~peterned/examples/csslayout1.html

http://themaninblue.com/experiment/footerStickAlt/

Do you have any specific HTML that we can see and play with?

Nick Presta
+2  A: 

This is one of the outright idiocies of CSS - I have yet to understand the reasoning (if someone knows, pls. explain).

100% means 100% of the container height - to which any margins, borders and padding are added. So it is effectively impossible to get a container which fills it's parent and which has a margin, border, or padding.

Note also, setting height is notoriously inconsistent between browsers, too.

Software Monkey
There is no reasoning, just what happened where browsers started to converge towards consistent behavior. Check out the book mentioned in my response.
Frank Schwieterman
A: 

Would it work for your situation to put the padding on the parent element?

David Zaslavsky
In the particular case I'm trying for the "parent element" is the document body, which I have to set to 100% height in order to have anything else size to 100% properly, so it's subject to the same problem. Thanks, though.
Toji
+5  A: 

According the the w3c spec height refers to the height of the viewable area e.g. on a 1280x1024 pixel resolution monitor 100% height = 1024 pixels.

min-height refers to the total height of the page including content so on a page where the content is bigger than 1024px min-height:100% will stretch to include all of the content.

The other problem then is that padding and border are added to the height and width in most modern browsers except ie6(ie6 is actually quite logical but does not conform to the spec). This is called the box model. So if you specify

min-height: 100%;
padding: 5px;

It will actually give you 100% + 5px + 5px for the height. To get around this you need a wrapper container.

<style>
    .FullHeight { 
       height: auto !important; /* ie 6 will ignore this */
       height: 100%;            /* ie 6 will use this instead of min-height */
       min-height: 100%;        /* ie 6 will ignore this */
    }

    .Padded {
       padding: 5px;
    }
</style>

<div class="FullHeight">
   <div class="Padded">
      Hello i am padded.
   </div
</div>
Alex
@Alex: But what makes the inner div (the Padded one) 100% of the outer div's height. My experience has been that you just get a little short div inside a larger full-height div.
Software Monkey
@Frank: Care to elaborate?
Software Monkey
@Software Monkey. You are correct i read the question wrong :) I thought the OP wanted 100% height of the page.
Alex
+47  A: 

I learned how to do these sort of things reading "PRO HTML and CSS Design Patterns". The 'display:block' is the default display value for the div tag, but I like to make it explicit. The container has to be the right type (position attribute is fixed, relative, or absolute).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html>
<head>

<style>
*.stretchedToMargin {
    display: block;
    position:absolute;
    height:auto;
    bottom:0;
    top:0;
    left:0;
    right:0;
    margin-top:20px;
    margin-bottom:20px;
    margin-right:80px;
    margin-left:80px;
    background-color: green;
}
</style>
</head>

<body>
<div class="stretchedToMargin">
Hello, world
</div>

</body>
</html>
Frank Schwieterman
Wow... that's a lot of CSS to go through but it appears to work! This may be what I was looking for. Let me play around with it in a few more scenarios before I mark this as accepted.
Toji
If you have more questions, like how to get it to work in a particular context, let me know. Note that the horizontal related elements and the vertical related elements can be used independently, so long as the display is block and position is fixed, absolute or relative.
Frank Schwieterman
Just tried it in the context that I'll be using it and it works like a charm. Still blown away that it takes that much code to do something that really ought to be two lines, but if it works I'll take it! Thanks!
Toji
I have been searching for this answer forever, a huge thank you.
deadroxy
Superb solution, will be bookmarking this one. Just quickly added it to http://jsfiddle.net/Rpdr9/ for anyone who wants a live demo. Hope you don't mind.
Nooshu
I think I need to look into that book now. Great solution.
Bart
Hmm actually I've come to prefer the book "CSS Mastery" by Andy Budd. Both are worth reading though. The patterns book has great information but the format is a pain. CSS Mastery is the only reference I've found that shows how to seal of a region containing floats though. Maybe its good to read both.
Frank Schwieterman
WOW!!! This answers a question I've had for literally *years*!!! This really made my day!
Rocketmonkeys
While @Toji didn't care about IE compatibility, I unfortunately have to. This solution didn't initially work under IE6. Adding Dean Edwards' [IE9.js](http://code.google.com/p/ie7-js/) to the page made this work. Now I just have to hope and pray that the relative/absolute positioning doesn't screw with something in a child element...
Christopher Parker
Awesome!! while(true) { Frank.awesomeness += 1; }
Q_the_dreadlocked_ninja
How did I not find out about this before! Thanks
Malachi
+3  A: 

The solution is to NOT use height and width at all! Attach the inner box using top, left, right, bottom and then add margin.

<style>
  .box {margin:8px; position:absolute; top:0; left:0; right:0; bottom:0}
</style>

<div class="box" style="background:black">
  <div class="box" style="background:green">
    <div class="box" style="background:lightblue">
      This will show three nested boxes. Try resizing browser to see they remain nested properly.
    </div>
  </div>
</div>
amolk
Up-vote because you're technically correct, but it's also basically the same answer as Frank's (Which I think is a bit more friendly to the sloppier browsers out there.)
Toji
A: 

CSS's width:auto behaves like a 100% 'margin box', meaning that it'll take up 100% of available space and any added margin, border or padding will not increase the space used.

Any other CSS width declaration, e.g. width:100% will behave like a 'content box', meaning that it'll take up the declared amount of space, plus the space declared for margins, border and padding.

Since your specific question pertains to an 100% width, you don't need the trickery I'm about to suggest (Frank Schwieterman's answer suffices entirely), but perhaps it'll save you from asking follow-up questions about 50%/etc boxes and help you understand the spec both now and in future: How To Design In The Box Model Of Your Choice. Note that the trickery described therein is only viable if you can afford to slightly break from semantic clarity in your HTML.

[Applies to height, too, harping on width for ease of reference.]

pinkgothic
+3  A: 

There is a new tag in CSS3 that you can use to change the way the box model calculates width/height, it's called box-sizing.

By setting this property with the value "border-box" it makes which ever element you apply it to not stretch when you add a padding or border. If you define something with 100px width, and 10px padding, it will still be 100px wide.

-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;

It does not work for IE7 and lower, however, I believe that Dean Edward's IE7.js adds support for it. Enjoy :)

Marco Jardim
That's awesome! Thanks for pointing this out!
Toji
A: 

Frank Schwieterman's answer didn't work for me. I copied the code exactly, then added many line breaks in the "hello world" content. I ended up with a colored background in the visible area, but then when I scrolled down, I ran into the same problem as with all other solutions -- the area beyond the initially-visible window did NOT have the background color. The solution works great as long as the entire page fits within the visible area of the browser window, but so would a simple "height : 100%".

Travis Low
I think that you're looking for a different effect than this question is asking for. The reasons why "height: 100%" doesn't work are explained in the question, and the idea is to get an element to fit it's parent exactly while still allowing some margin or padding, which Frank's solution does. If you're trying to get a background color on the entire page, why not just set the background for the body tag?
Toji
Actually, no. I was trying to get a band down the side of the page, with some padding at the top and bottom. I can't remember everything I tried at this point, but I did want an image -- I just switched to bg color to simplify things a bit. The code posted by Frank works beautfully as long as the desired size fits within the browser window. I solved the problem using similar markup (div right inside body), but I had to add some extra CSS to keep it from stopping at the window boundary.
Travis Low