views:

437

answers:

5
+3  A: 

The margins are being merged. The output produced by IE is probably incorrect.

In the specifications (which are down for me at the moment):

Two or more adjoining vertical margins of block boxes in the normal flow collapse. The resulting margin width is the maximum of the adjoining margin widths.

You can use borders instead of margins, with border-color set to transparent.

strager
+2  A: 

There is a pretty fitting answer to this question: overflow: auto;

<html>
<head>
<style>
body { margin:0; padding:0; }
.outer
{
    background-color: #00f;
    height: 50px;
    overflow: auto;
}
.inner
{
    height: 20px;
    width: 20px;
    background-color: #f00;
    margin: 10px 0 0 10px;
}
</style>
</head>
<body>
<div class="outer">
    <div class="inner">
    </div>
</div>
</body>
</html>
Chris Lloyd
This may cause scrollbars, which may or may not be desired. -1 for possible side effects.
strager
In that case you can use overflow: hidden;
Chris Lloyd
That causes yet another side effect; what if the content wanted to overflow? I think the overflow solution is a bug in the browser regardless; I can't see anything in the specifications about overflow and non-negative margins.
strager
The following two sets of rules determine which margins collapse: Margins of a box with ‘overflow’ other than ‘visible’ do not collapse with its children's margins. http://www.w3.org/TR/css3-box/#collapsing-margins
Jimmy
A: 

Could it be IE sees the DOM as div.inner having div.outer as it's parent node(and calculates offset from it),
and that other browsers instead has both of them answering to the body element?

Morten Bergfall
A: 

Ok, solution without overflow auto:

<html>
<head>
<style>
body { margin:0; padding:0; }
.outer
{
    background-color: #00f;
    height: 50px;
    border: 1px solid transparent;
}
.inner
{
    height: 20px;
    width: 20px;
    background-color: #f00;
    margin: 10px 0 0 10px;
    padding: 0;
}
</style>
</head>
<body>
<div class="outer">
    <div class="inner">
    </div>
</div>
</body>
</html>

The inner element is wanting something to push against, and providing a boder (or forcing the browser to consider the overflow) does this.

Chris Lloyd
problem is, now you have a 1px transparent border :) probably works better if you set it to 1px solid #00f.
Jimmy
+4  A: 

As much as strager's answer already explains about as much as you need to know as to why it happens – namely that it happens the way it does in browsers other than IE because the specs say so – I think he picked the wrong quote from the section of the CSS 2.1 specification about collapsing margins.

The point he quoted explains how margins can collapse, not how they can "move" to a parent element.

This is rather what explains it:

  • If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.
    • If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.

Or, in slightly more human-readable form in the Mozilla developer documentation:

Parent and first/last child:

If there is no border, padding, inline content, or clearance to separate the margin-top of a block with the margin-top of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate the margin-bottom of a block with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.

As for how to fix it, I'd probably go for the overflow: auto solution Chris Lloyd suggested (as much as that may have side-effects).

But then that really depends on what exactly the rest of your code looks like. In this simple example you could easily just change the margin on the child element to a padding on the parent element.

Or you could float the child element, or absolutely position it...

Or how about an inverse clearfix if you want to get really fancy:

.outer:before {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
mercator
Yeah, I didn't do much research in answering this question. Seems you're less lazy than I am. Thanks for clarifying.
strager
I was lucky you got the wrong quote or I'd have dived into the specs for nothing. ;)
mercator