开发者

margin-top not working with clear: both

<div style="float: left;">Left</div>
<div style="float: right;">Right</div>
&开发者_如何学Golt;div style="clear: both; margin-top: 200px;">Main Data</div>

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


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.


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.


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:

margin-top not working with clear: both

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:

margin-top not working with clear: both

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!


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.


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/

margin-top not working with clear: both

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.


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


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.


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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜