77

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

Why is the margin:top for 'Main Data' not working in the code above?

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Vishal
  • 19,879
  • 23
  • 80
  • 93
  • which browser are you using? I have checked it using firefox and IE and chrome as well. – Zain Shaikh Nov 16 '10 at 19:51
  • 5
    Most of the answers here provide workarounds for this behaviour, and that's fine, but if you want a (long, difficult) answer that actually explains *why* it happens in the first place, see [mine](http://stackoverflow.com/a/41335816/1709587). – Mark Amery Dec 26 '16 at 20:24
  • Most of the answers have said 'HOW' not 'WHY'! – Ayub Dec 06 '20 at 16:50

8 Answers8

74

You could put the two floated divs into another one that's got "overflow: hidden" set:

<div style='overflow:hidden'>
  <div style="float: left;">Left</div>
  <div style="float: right;">Right</div>
</div>
<div style="clear: both; margin-top: 200px;">Main Data</div>

edit — To add a bit to this 5-year-old answer: I think the cause of the confusing behavior is the somewhat complicated process of margin collapse. A good trick with the original HTML from the OP is to add a CSS rule like this:

div { border: 1px solid transparent; }

Poof! Now (without my additional <div>) it works fine! Well, except for that extra pixel from the borders. In particular, I think it's a combination of the way that clear: both works and the margin collapse rules that result in the unexpected layout from the code in the OP.

edit again — For the complete (and, I think, completely accurate) story, see Mark Amery's excellent answer. The details have some complexity that this answer glosses over.

Community
  • 1
  • 1
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Can you please elaborate it more? – Salman Virk Feb 22 '13 at 16:52
  • @user395881 well the margin calculations the browser performs are concerned with elements that are in the same layout context. For floated elements, margins are figured with respect to other local floated elements and (I think) with inline content too. But these static block elements are in a different context. The layout rules are complicated and hard to understand, I agree. The W3C documents have some diagrams that explain some stuff. – Pointy Feb 22 '13 at 17:14
  • The floated elements effectively not being "there" is patently incorrect as an explanation for the margin not working. If that were all there was to it, then we would expect to simply see the top of the cleared div's margin box beginning at the top of the parent element instead of at the bottom of the floated elements. Instead, we see the margin not taking effect *at all*; the cleared div in the OP's original code ends up positioned *higher* than it would have were the floated div's *actually* not there. Your solution works, but your explanation of the problem's cause cannot possibly be right. – Mark Amery May 16 '15 at 22:41
  • @MarkAmery I'm really not sure I see what you're talking about. – Pointy May 17 '15 at 03:30
  • @Pointy The div with margin-top is positioned differently given the OP's code than it would be if the floated divs were deleted or given position:absolute. This means that the effect cannot be down to the floated divs not really being "there" for the purposes of margin calculation. – Mark Amery May 17 '15 at 08:48
  • @MarkAmery hmm well for whatever reason, neither the OP nor my answer work (for me) as snippets. It might be that what's involved here is the sometimes weird effect of top margin "collapse". – Pointy May 17 '15 at 13:30
  • @MarkAmery this is an interesting question. Without `clear: both` I'm pretty sure that it's top margin collapse that's scooting the floated elements down. I'm not sure however what makes the margin have no effect *with* `clear: both`. – Pointy May 17 '15 at 13:39
  • @Pointy The snippets worked for me this morning but not any where. I think it's temporary and that http://meta.stackoverflow.com/questions/294512/snippets-are-down, though I'm waiting for confirmation. EDIT: Snippets just came back up - I think you'll be able to run them again now. – Mark Amery May 17 '15 at 13:58
  • @MarkAmery yes StackExchange was having some site issues. Anyway, after reading the W3C spec about `clear` I'm pretty sure that it's a margin collapse effect. I don't entirely understand it however. I edited a bit more into the answer. – Pointy May 17 '15 at 14:53
  • @Pointy margin collapse is part of the explanation, but it's a bit more complicated than that. See [the answer I've just added](http://stackoverflow.com/a/41335816/1709587). Perhaps tweak this answer to just offer the workaround and not try to explain the behaviour? The explanation you currently give is half wrong (the first paragraph) and half unsatisfyingly incomplete (the edit). – Mark Amery Dec 26 '16 at 20:15
  • @MarkAmery I'm not surprised the answer has accuracy problems; I never felt like I completely understood what was going on. I added the bit about margin collapse because I had encountered that issue myself. I'll read over your answer and make an update. – Pointy Dec 26 '16 at 20:47
23

While Pointy shows how you can wrap the floats in a div, alternatively you can insert an empty div between the floats and the main data section. For example:

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both;"></div>
<div style="margin-top: 200px;">Main Data</div>

This might prove useful in cases where adding a div wrapper around some HTML is not desirable.

Randall Cook
  • 6,728
  • 6
  • 33
  • 68
21

The logic behind this in the spec is mind-bending, and involves a complicated interaction of the rules for clearance and collapsing margins.

You're probably familiar with the conventional CSS box model, in which the content box is contained within a padding box contained within a border box contained within a margin box:

Diagram of box model

For elements with clear set to something other than none, an additional component may be introduced to this model: clearance.

Values other than 'none' potentially introduce clearance. Clearance inhibits margin collapsing and acts as spacing above the margin-top of an element.

In other words, the box model in those cases really looks more like this:

Box model with 'clearance' added above the top of the margin box

But when is clearance introduced, and how big should it be? Let's start with the first of those questions. The spec says:

Computing the clearance of an element on which 'clear' is set is done by first determining the hypothetical position of the element's top border edge. This position is where the actual top border edge would have been if the element's 'clear' property had been 'none'.

If this hypothetical position of the element's top border edge is not past the relevant floats, then clearance is introduced, and margins collapse according to the rules in 8.3.1.

Let's apply this logic to the question asker's code. Remember, we're trying to explain the position of the third div in the code below (backgrounds added to aid in visualisation):

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

Let us imagine, as the spec asks us to, that clear is set to none on the third div, instead of both. Then what would the snippet above look like?

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

Here, the third div is overlapping the two floated divs. But wait; why is this so? Sure, it's allowable for floated elements to overlap block-level ones (per the Floats spec, "Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float did not exist."), but our third div has loads of margin-top on it, and comes after the two floated divs; shouldn't the two floated divs appear at the top of the body, and the third div appear 200px down, well below them?

The reason this doesn't occur is that the margin of the third div collapses into the margin of the divs' parent (in this case, the body - but the same behaviour happens if you wrap all three divs in a parent div). The Collapsing margins spec (quoted below with several irrelevant details omitted) tells us that:

Adjoining vertical margins collapse...

Two margins are adjoining if and only if:

  • both belong to in-flow block-level boxes that participate in the same block formatting context
  • no line boxes, no clearance, no padding and no border separate them ...
  • both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
    • top margin of a box and top margin of its first in-flow child
    • ...

The third div in our example certainly isn't the body's first child, but it is its first in-flow child. Note that per https://www.w3.org/TR/CSS22/visuren.html#positioning-scheme:

An element is called out of flow if it is floated, absolutely positioned, or is the root element. An element is called in-flow if it is not out-of-flow.

Since the first and second div in our example are floated, only the third div is in-flow. Thus its top margin adjoins the top margin of its parent, and the margins collapse - pushing down the entire body, including the two floated elements. Thus the third div overlaps its siblings despite having a large margin-top. Consequently - in this hypothetical case, where the third element's clear is set to none - we satisfy the condition that:

the element's top border edge is not past the relevant floats

Thus:

clearance is introduced, and margins collapse according to the rules in 8.3.1

How much clearance? The spec gives browsers two options, with a couple of clarifying notes:

Then the amount of clearance is set to the greater of:

  • The amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.
  • The amount necessary to place the top border edge of the block at its hypothetical position.

Alternatively, clearance is set exactly to the amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.

Note: Both behaviors are allowed pending evaluation of their compatibility with existing Web content. A future CSS specification will require either one or the other.

Note: The clearance can be negative or zero.

Before we can start applying these rules, we immediately hit a complication. Remember that collapsing margin that we had to take into account in the hypothetical case where clear was none? Well, it doesn't exist in this non-hypothetical case where we're calculating the clearance to use, because the existence of the clearance inhibits it. Recall the collapsing margin rules from 8.3.1, quoted earlier, dictate that margins are only adjoining if:

  • no line boxes, no clearance, no padding and no border separate them

(emphasis added). As such, the third div's top margin and the top margin of its parent are no longer adjoining. We can simulate this pre-clearance scenario in our example snippet by keeping clear: none but adding padding-top: 1px to the body, which also disables margin collapse, per the rule quoted above.

body {
  padding-top: 1px;
}
<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: none; margin-top: 200px; background: blue;">Main Data</div>

Now, unlike when the margins were collapsed, our third div is comfortably below its two floated siblings. But we have already decided, based upon a hypothetical scenario where the margins did collapse, that clearance must be added; all that remains is to choose the amount of clearance, in order to:

place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared

And so we have no choice but to apply a negative clearance to the third div, in order to drag its top border edge up to touch the bottom outer edge (also known as margin edge) of the floated elements above it. As such, if the floated elements are each 10px high and the third div has 200px of top margin, -190px of clearance will be applied. That, at last, gets us to the final result seen by the question asker:

<div style="float: left; background: red;">Left</div>
<div style="float: right; background: green;">Right</div>
<div style="clear: both; margin-top: 200px; background: blue;">Main Data</div>

(Note that if you inspect the third div in the snippet above using your browser's dev tools, you will still be able to see the 200px of top margin above the div, going way out above all the rest of the content - it's just that the entire margin box has been hauled upwards by the large negative clearance.)

Simple!

Community
  • 1
  • 1
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • This - well, I was going to say "makes sense" but that's not quite right - is certainly the accurate story. I think I came to the conclusion without doing anything close to as much work as this that there's no way to introduce a "margin collapse fence" that has no layout impact (0px "thickness"). Is that right? – Pointy Dec 26 '16 at 20:54
  • @Pointy For the container/child collapse case, `overflow: auto` or `overflow: hidden` on the parent container should do the trick, according to the big **Note** in the spec at https://www.w3.org/TR/CSS22/box.html#collapsing-margins and also according to popular answers like http://stackoverflow.com/a/6204990/1709587 (although for some reason it doesn't work if the container is the `body` element). I'm not quite sure why this works, because it depends upon the definition of a "block formatting context" (which I haven't looked into and don't currently understand). – Mark Amery Dec 26 '16 at 22:40
  • @Pointy using a fence of e.g. 0.02px seems to work as well (at least in Chrome - might be browser-specific), despite having no visible impact on layout. Smaller fences like 0.01px don't work, however. Why? No idea. – Mark Amery Dec 26 '16 at 22:44
  • Yea I half-remember trying smaller and smaller border widths (etc) when I was working on my own version of this problem, until I got sick of it and decided I didn't really care that much. It certainly is a surprising set of behaviors. – Pointy Dec 26 '16 at 23:00
  • From the spec: "Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children." As to why it was made that way (like so many other BFC-related changes from CSS2 to CSS2.1), I don't know for sure, but [I made an educated guess](http://stackoverflow.com/a/37473127). The reason it doesn't appear to work with the body element is because [the body element loses its overflow property to html when html has overflow: visible](http://stackoverflow.com/a/15462231). – BoltClock Dec 27 '16 at 04:00
7

Pointy and Randall Cook have excellent answers. I thought I'd show one more solution.

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="float: left; clear: both; margin-top: 200px;">Main Data</div>

If you make the 3rd element "float: left;" AND "clear: both;", it should have the desired effect of giving the 3rd element a 200 pixel margin. Here's a link to an example.

This also might affect other followup elements as to whether they need to be floats or not. However, it might also have the desired effect.

rvbyron
  • 179
  • 1
  • 4
4

Alternative solution:

You can actually put a margin-bottom on the floated elements to push DOWN the element underneath that has clear: both.

http://jsfiddle.net/9EY4R/

enter image description here

Note: Having made this suggestion I have to immediately retract it as not generally a good idea, but in some limited situations may appropriate;


<div class='order'>

    <div class='address'>
        <strong>Your order will be shipped to:</strong><br>
        Simon</br>
        123 Main St<br>
        Anytown, CA, US
    </div>

    <div class='order-details'>
        Item 1<br>
        Item 2<br>
        Item 3<br>
        Item 4<br>
        Item 5<br>
        Item 6<br>
        Item 7<br>
        Item 8<br>
        Item 9<br>
        Item 10<br>
    </div>

    <div class='options'>
        <button>Edit</button>
        <button>Save</button>
    </div>
</div>

The panel with items is called order-details with this css

.order-details
{
    padding: .5em;
    background: lightsteelblue;

    float: left;
    margin-left: 1em;

    /* this margin does take effect */
    margin-bottom: 1em;
}

In the above fiddle - the yellow panel has a margin-top, but unless it is greater than the tallest floated item then it won't do anything (of course that's the whole point of this question).

If you set the margin-top of the yellow panel to 20em then it will be visible because the margin is calculated from the top of the outer blue box.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
3

Use 'padding-top' in your main data div instead. Or, alternatively, wrap the main data div in one with 'padding-top'.

Colin R. Turner
  • 1,323
  • 15
  • 24
0

Sometimes a combination of position relative and margin can solve these type of problems.

I use this technique for my alignright and alignleft classes in WordPress.

For instance if I want a "bottom margin" that is respected by clearing elements you can use.

.alignright{
   float: right;
   margin-left: 20px;
   margin-top: 20px;
   position: relative;
   top: -20px;
}

For your example you could do something like

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
<div style="clear: both; margin-bottom: 200px; position: relative; top: 200px;">Main Data</div>
christian
  • 2,279
  • 4
  • 31
  • 42
0

Try setting a bottom margin on one of the floated elements. Alternatively, you can wrap the floats in a parent element, and use a css hack to clear it without additional markup.