Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS: clean solution to the margin collapse issue when floating an element

Tags:

Example HTML+CSS:

<html>   <body style="padding: 0; margin: 0;">     <div style="float: right;">first</div>     <div style="margin-top: 2em;">second</div>   </body> </html> 

Desired behavior: the first div floats to the top-right of the window. Actual behavior: it floats 2em below the desired position. Reason: margin collapsing.

Despite identifying the problem, the solutions I can come up with feel like hacks:

  • change body style to margin: -1px 0 0 0; border-top: 1px solid;.
  • insert <div style="height: 1px; margin-bottom: -1px;"></div> before first
  • insert the above <div> between the first and second

Is there a clean, idiomatic way of avoiding this issue?

like image 892
jameshfisher Avatar asked Jun 01 '11 16:06

jameshfisher


People also ask

How do I fix the collapse margin in CSS?

How to Avoid Margin Collapse. First, remember that margins should preferably be used to increase the distance between sibling elements, not to create spacing between a child and a parent. If you need to increase space within the Flow layout, reach first for padding if possible.

Do margins on floated elements collapse?

Note that the margins of floating and absolutely positioned elements never collapse.

Is CSS margin collapsing only happens with?

Margin collapsing only happens to block-level elements. Other than block-level elements no other elements margin can collapse. Here, we have two <p> tags that were inline-block, hence their margin does not collapse. Margin collapsing occurs only when the block elements come in direct contact with each other.

What causes container collapse CSS?

This happens because elements with float property are removed from the document flow so the sizes stay unknown for the parent element (as nothing is contained in it) so it is set to 0 .


2 Answers

Adding overflow: hidden; to the body should solve your problem. It defines a new block formatting context as described in this article: The magic of overflow: hidden.

jsFiddle Demo (the body tag is automatically added by jsFiddle, that's why I haven't included it in the HTML markup)

UPDATE (thx to @clairesuzy): This solution does not work if body has padding: 0. Until I can find a better way, I can only suggest to add a wrapper around the two divs (at least I deserve now @Marcel's downwote :)), which I still think is cleaner than the solutions posted by the OP. I normally add a wrapper around floated stuff anyways (makes it easier to handle older browsers most of the time), most of the time it does not need to be added deliberately, because it is logically and semantically required.

So for now, I can come up with this:

<body style="padding: 0; margin: 0;">    <div id="container" style="overflow: hidden;">        <div style="float: right;">first</div>        <div style="margin-top: 2em;">second</div>    </div> </body> 

jsFiddle Demo

UPDATE 2: After thinking it through, and reading comments, I really think that overflow: hidden (or anything other than overflow: visible) on the container is the right solution. The only exception where it did not work for me is setting it on the body element, which is a very rare situation anyways. In these rare situations, you can try using position: absolute; top: 0; right: 0; instead of floating.

Another possible workaround: I have also found that setting display: inline-block; width: 100%; on body works indeed.

jsFiddle Demo

like image 156
kapa Avatar answered Sep 21 '22 03:09

kapa


SOLUTION

add overflow: auto; or overflow: hidden; or overflow: scroll; to both html and body tag.

IF YOU WANT TO KNOW WHY

Creating block formatting context(BFC) on body tag.

Why it does not work if I add overflow: hidden; only to body?

Here is why:

W3C#block-formatting

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

W3C#overflow:

UAs must apply the 'overflow' property set on the root element to the viewport.

When the root element is an HTML "HTML" element or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, user agents must instead apply the 'overflow' property from the first such child element to the viewport, if the value on the root element is 'visible'.

The 'visible' value when used for the viewport must be interpreted as 'auto'.

The element from which the value is propagated must have a used value for 'overflow' of 'visible'.

the first such child element and The element from which the value is propagated refer to body tag in this case.

In other words, if you add overflow: hidden; or overflow: auto; or overflow: scroll; to body while the value of html's overflow property is visible, the value of body's overflow property(hidden or auto or scroll) will be propagated to the viewport. So according to the first W3C quote, body would not establish new block formatting context.

However, if you add overflow: hidden; or overflow: auto; or overflow: scroll; to html, the overflow value of 'body' would not be propagated and hence establish new block formatting context.

like image 37
medifle Avatar answered Sep 23 '22 03:09

medifle