alt131 - 6:55 pm on Jul 31, 2011 (gmt 0)
Hi vampke, welcome to css, and thanks for a code sample that illustrates the issue :)
I’m joining the party late as it took all last week to undo an engineers plan that a group of us would be telecommunications-free for the conceivable future. Now I discover everyone was having fun chasing margins. No fair ;)
The previous posts identified the issues, what follows is my attempt to explain the “why” – but can you tell us what you actually did to “solve” the problem in this code – was it just setting margin:0 on <p>?
I managed to solve my problem but I dont understand what the footer has to do with the whitespace between header and content.
Could anyone explain?
Like Paul I think the “duplicated” margin is a bug in ff, but note legacy versions of Op and ie produce the same layout. I don’t think the recommendations are clear, they have changed, and the re-wording has introduced interpretation issues. So while the duplication is wrong and there has been at least one abandoned attempt to re-write gecko to deal with margin issues, it may actually be caused by trying to satisfy conflicts within the recs themselves. Seems much easier to avoid the issue by using padding on the container rather than margins on the child.
But why is it happenings? As already identified, this is about collapsing margins [w3.org]. Specifically, the default vertical margin:1em on div#footer p – which is why margin-top:0 resolved the “gap”. Margins adjoin (and therefore may collapse) when they belong to in-flow block-level boxes in the same formatting context, and there is no border, padding, content or clearing to separate them. So display:inline corrected the gap because the margins ceased to adjoin – not because it was a display issue per se. Setting overflow:hidden on the clearing div works for the same reason, as does the position in Rocknbil's example.
In addition the margins must form specified pairs – so Paul I’m not sure the complete explanation is that the margin on div#footer p is collapsing onto div#footer, and then onto the parent div#bg as that wouldn’t create the necessary adjacent pair. Plus the clearing div is in the same block formatting context so they are separated by clearance. So I suspect the issue also involves the float, the clearing div and the relationship between <p> and its parent div#footer.
div#footer has height:100px, while the padding on div#footer p dimensions it much taller so it overflows. Margins may adjoin and collapse even if the elements are not adjacent in the html, so that means the margin on div#footer p is not collapsing with the parent, but with the margins of other elements. In this code the next adjacent margin is div#content and rolling over the “layout” diagram with the shade function activated in firebug and dragonfly indicates the top margin on the <p> is adjoining/collapsing with the bottom margin:0 on div#content.
For non-ff modern versions the collapse stops there, but that doesn’t explain why, or why the margin “bubbles” up to cause a gap between div#header and div#content in ff – or older ie and opera.
Per 9.5.2 clear:both “inhibits” margin collapsing and acts as “spacing” above the margin-top of an element. The spacing is computed by calculating the hypothetical position of the clearing div’s top border edge as if there was no clear. Expose that by setting a height and clear:none: The clearing div is positioned above div#content_col1 because it is floated and removed from the flow so other boxes flow vertically as if it did not exist. Because the margins adjoin, the clearing div has collapsed through its parents so the top border edge is in the same place as the top border edge for div#bg.
However, the point of a clear is to clear floats, so 9.5.2 specifies that if the hypothetical position does not put the clearing element below the floated element, the user agent should collapse margins per 8.3.1, then introduce clearance.
Clearance is a “space” above margin-top and 9.5.2 provides the rules for calculating its length. However, there are two different calculation options, and both are permitted. One would place the top border edge even with the bottom of the float as most of us would expect. The other is to place the top border edge in its hypothetical position – which ff seems to be doing.
The clearing element
So now factor in the clearing div, which has a min-height and height of zero (or height: auto in ie) so per 10.6.3 computes to height:0. It has no borders or padding and no content, so its own margins adjoin and collapse. Per 8.3.1
Note the older recs did not limit “collapsing through” to only zero-height elements and I’m not convinced the limitation was intended for the future. But most clear “fixes” prevent this by techniques such as setting a height on the clearing element, or putting content in a pseudo element.
If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it.
Why a clear can collapse, but still clear
As penders referred, the clearing div seems to collapse, but is still clearing. That is because clears clear floats, not margins. Clearance does not affect the position of the elements, only the calculations for the collapsing margins. (It does affect the layout of descendants, but there aren’t any in this code. ) Clearance may be zero, or even negative – it will still act to “clear” the float.
Explaining what we see
If clearance is calculated to place the top border edge even with the bottom of the float, the zero-height clearing div is immediately below the float. The clear clears the float and inhibits margin collapse, so no margins collapse above that point. div#content has bottom margin:0 - and we can observe it collapsing with the 1em margin on <p>.
However, if clearance is calculated to place the top border edge in its hypothetical position, and the clearing div has zero-height, both the top and bottom border edge are in the same position – as above, the top border edge for the parent div#bg. That has implications for div#content because the float is removed from the flow, and the clearing div has zero-height and/or the top margin does not collapse with div#content’s bottom margin. That means div#content is treated as having height:0, which positions its bottom border edge in the same place as the bottom border edge for the clearing div. As the clearing div has zero-height, that is the same place as the top border edge. And that means the top margin on <p> collapses through to the top margin of div#bg. Hence the gap in ff and legacy versions.