Why floating causes increased margin at the top? - css

Simple CSS question, I've been wondering for a while and I'd like to understand exactly how this works.
Let's say I have the following:
<div>
<p>some text</p>
<p style="float: left">some text</p>
</div>
If I remove the float: left from a given <p> element, the element and its previous sibling are stacked closer.
Why floating an element causes this increased margin at its top?

The reason for the difference is a behaviour known as collapsing margins.
Note that paragraphs have, by default, a user-agent defined top and bottom margin.
When the second paragraph does not have float: left, the bottom margin of the first paragraph and the top margin of the second paragraph are adjoining and so collapse into each other.
When the second paragraph has float: left, those two margins will no longer collapse into each other; they are no longer considered adjoining because:
Two margins are adjoining if and only if:
both belong to in-flow block-level boxes that participate in the same block formatting context
[...]
Following the "block formatting context" link reveals that:
Floats [...] establish new block formatting contexts for their contents.
The spec goes on to say:
Note the above rules imply that:
Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
[...]

This doesn't occur for me on Chrome19.. but this would occur because of your browser's default stylesheet. If you want to prevent against this happening, use margin: 0; padding: 0; on the floated element.

Related

how `clear` prevent margin collapsing?

margin collapse is disbled in the following case:
If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.
what does it mean?can u give me some concrete examples?
This ensures that a clear prevents any following elements from overlapping the floats.
Let's start with floats and clear for now. Floats can overflow their parent:
<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
That red box overflows!
</div>
If we add a clearing div, it never will. A clear is like saying nothing else can flow above this line:
<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<div style='clear:both;'></div>
<!-- Anything down here will not overlap the floats -->
</div>
However, margin collapsing breaks things a little, because following elements can collapse 'through' something, going all the way up to the very top of the top margin. Let's have a quick excursion into certain aspects of margin collapsing.
Self collapsing hacks
In general, margin collapsing applies to any top margin which is directly touching any bottom margin.
That includes an elements own top/bottom margins too. This is called self-collapsing, and margin collapsing happens repeatedly. Here's a quick example of both of these things together:
<div style='margin-top:30px; margin-bottom:30px;'></div>
<div style='margin-top:30px; border:1px solid black;'>
The gap above me is only 30px, not 90!
</div>
That first div entirely self-collapses, resulting in a computed space of 30px, then the second div collapses into that too, keeping the space at just 30px.
Ok, so we've now got a rough idea of what self-collapsing is. Now let's start trying to abuse that with a self-collapsing clearing div:
<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<div style='clear:left;margin-top:90px;margin-bottom:90px;'></div>
I'm after the clear and look, no 90px gap!
</div>
The margin is still there though. It actually runs 90px upwards over the floats.
Next, imagine there was no text after it, and the parent had a bottom margin. By our margin collapsing rules, it should collapse upwards. Sibling elements might even collapse 'through' it, all the way up to the top. We don't want that, because it would cause some unwanted overlapping.
This part of the specification blocks this behaviour. Let's break the spec's language down to make that clearer:
If the top and bottom margins of an element with clearance are adjoining
This is describing a self-collapsing element which has cleared a float.
its margins collapse with the adjoining margins of following siblings
It's fine for other margins to collapse into it, but..
That resulting margin does not collapse with the bottom margin of the parent block.
..the very bottom margin must not collapse upwards, because that would result in our awkward overlapping case.
Here's an example of the case where the rule is applied:
<div style='border:1px solid green;'>
<!-- A parent block with a bottom margin, and no border -->
<div style='margin-bottom:50px;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<!-- A self collapsing cleared div -->
<div style='clear:left;margin-top:50px;margin-bottom:50px;'></div>
<!-- The parents bottom margin is adjacent to our collapsed margin,
but it gets blocked from collapsing upwards. We see a gap here -->
</div>
</div>
Adding some text into that clearing div makes it no longer self collapse, but its bottom margin then safely collapses with the bottom margin of the parent instead.

Why isn't the background css color showing?

Can anybody explain why no background color is displayed in the outermost div in the space of the inner div's margin?
<div style="background-color:yellow;">
<div style="margin-top:10px;background-color:black;color:white;">
Why isn't the background color yellow inside my top margin?
</div>
</div>
Divs are block elements, but they take up no space on their own (other than creating a line break) so your inner div is filling all available space within the outer div, masking the yellow background. Add some padding to the outermost div and you will see the yellow.
This is known as "margin collapse".
In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.
As found in other answers, adding padding or border to the parent will prevent the margins collapsing.
I also had success applying the following CSS to the container, based on tarkabak's method described here. (Please note limited browser compatibility of :before and :after.)
.prevent-margin-collapse:before,
.prevent-margin-collapse:after
{
content: "\00a0"; /* No-break space character */
display: block;
overflow: hidden;
height: 0;
}
<div style="background-color:yellow;" class="prevent-margin-collapse">
<div style="margin-top:10px;background-color:black;color:white;">
Why isn't the background color yellow inside my top margin?
</div>
</div>
http://jsfiddle.net/yCHkW/
In addition to the other answers: This is a matter of collapsing margins. The section "Collapsing Margins Between Parent and Child Elements" should apply in this specific case.
Update: Here's a statement regarding this topic taken directly from the box model specification of CSS3 (you can find almost the same sentence within the CSS2 specification as well):
Certain adjoining margins combine to form a single margin. Those margins are said to “collapse.” Margins are adjoining if there are no nonempty content, padding or border areas or clearance to separate them.
To achieve what you want to see change your html as followed:
<div style="background-color:yellow; padding-top:10px;">
<div style="background-color:black;color:white;">
Why isn't the background color yellow inside my top margin?
</div>
</div>
The reason is that the outer div has no width set and just takes the size of its content.
I would imagine it has something to do with not inheriting any properties from elsewhere.
<div style="background-color:yellow; position: fixed;">
<div style="margin-top:10px;background-color:black;color:white;">
Why isn't the background color yellow inside my top margin?
</div>
</div>
http://jsfiddle.net/rJ3HG/

How do nested vertical margin collapses work?

I am having a hard time grasping the concept of vertical margins collapsing in nested elements. I came an article at http://www.howtocreate.co.uk/tutorials/css/margincollapsing explaining how it works however am confused by its explanation. So in its example it cites that there are 2 elements as follows
<div style="margin-top:10px">
<div style="margin-top:20px">
A
</div>
</div>
Seeing that the inner div has a margin of 20px, that is what will be applied for the entire block of code. What confuses me is everything after that and not yet looking about issues with Internet Explorer 7. Would someone be able to explain it for a complete newbie to CSS in a simplified manner?
Two-ish rules to remember:
If margins touch, they collapse.
Nested items "snuggle" if only margin separates them.
Elements outside the "Flow" behave differently. That is, this behavior does not apply the same to floated, or position:fixed, or position:absolute elements.
So for this HTML (nested divs) :
<div id="outer">
<div id="inner">
A
</div>
</div>
and this initial CSS:
#outer {
margin-top:10px;
background:blue;
height: 100px;
}
#inner {
margin-top:20px;
background:red;
height: 33%;
width: 33%;
}
The margin collapses to the max of the touching margins and the nested div "snuggles" to the start of the container, like so: (See it at jsFiddle.)
But, the moment the two margins are separated -- by a border or by preceding content in the container, for example -- the margins no longer touch, so they no longer collapse.
EG, just a little, non-breaking white-space , like so:
<div id="outer">
<div id="inner">
A
</div>
</div>
kills the collapse : (See that at jsFiddle.)
Using a border, instead of leading text : (Fiddle)
A diagram may help:
In case it wasn't obvious: blue = outer div, red = inner div; I've drawn them with constant height and horizontal positioning. You can work out what happens if the height is fitted to the contents etc.
The "Before collapsing" column shows what you get if the margins aren't considered adjacent, e.g. if you draw the border of the blue/outer div; but if there is no border, then you get the "After collapsing" column. The top row switches the two margins around from the example, because I think the behaviour in this case is more intuitive; the bottom one shows the example at howtocreate and is consistent with the top row.
Two-ish rules to remember:
If margins touch, they collapse. Nested items "snuggle" if only margin separates them. Elements outside the "Flow" behave differently. That is, this behavior does not apply the same to floated, or position:fixed, or position:absolute elements.
Brock Adams is correct, but I also wanted to add that "overflow:hidden" can also prevent nested margins from collapsing.

Why is this block div affecting two floated divs?

Suppose I have three <div>s in my page,:
<div id="left" class="test" style="float:left;"></div>
<div id="right" class="test" style="float:right;"></div>
<div id="footer">footer</div>
with this css:
.test{ background:black;height:200px;width:200px;}
#footer{ background:yellow;margin:20px 0 0 0;}
What I want is:
let the "#left" float to left
let the "#right" float to right
change nothing about the "#footer", just set it to margin: 20px;
The result is below:
But I wonder why the floated divs also have the same margin as the #footer. They are floated, so they're independent of the other elements, why would the #footer could affect them?
as well as clear:both on the footer, just adding a container "wrapper" div around the the elements will stop this happening - example
actually adding clear: both; on the footer won't give you a 20px gap between the floats and the footer either, you would actually need to add the 20px bottom margin to the floats - the reasons are all linked.. to clearance or non clearance and it's interaction with Collapsing Margins
Why?
You said you wanted to know why this is happening, in your OP scenario it's because of Collapsing Margins.
You have no clearance involved in the original example, so yes the floats are removed, So the footer margin is still adjoining, therefore collapsing with, the body element, so the body element is the one getting the margin, and then because the floats are still actually inside the body they get the margin too.
As I mentioned above creating a wrapper div to "contain" the floats stops this happening because the rules of collapsing too. However you choose to contain the floats, either with overflow:hidden, or by floating the "wrapper" stops this interaction because .. from the section on collapsing margins:
Vertical 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.
you see that both of the properties, float and 'overflow other than visible' are the means to "contain floated children" - actually they're establishing a new block formatting context, but in easy speak most know it as "containing floats" ;)
Now once you have that, that fixes your first bit but then if you decide to introduce clear:both on the footer, the modern browsers will not put a 20px margin between the floats and the footer.. this is actually correct.. from the section on the clear property (my bold):
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.
In order to place the top edge of the footer below the floats (in your example) the browser has to introduce 200px of clearance, which is far more than 20px so it follows rule 1. If your top margin on the footer was 220px, the margin would be greater than the any clearance needed, so it would follow rule 2.
So, if you did actually want the footer to be 20px below the floats no matter what their heights are, you would put the 20px as a bottom margin onto the two floats, so it [the footer] would clear, via clearance rule 1, the floats with the required gap/margin, no matter which was float the longest.
PS: Don't test the above in IE7 or below - and I hope it wasn't too boring ;)
Add a clear: both to the #footer CSS. That should make the footer render below the floating divs with the margin you want.
Try this and this may solve your problem:
<div id="right" class="test" style="float:right;"></div>
<div id="left" class="test"></div>
<div id="footer">footer</div>
CSS remaining unchanged.
I made a test before finding this page that has two boxes, with the right-floated one being affected by the left block one (which comes after it) here: http://jsfiddle.net/4r75s/
The overflow trick that prevents parent divs collapsing when they only contain floated content seems to work here, that is setting overflow to hidden, auto or scroll. I wrapped them in a containing div to do it and it works: http://jsfiddle.net/4r75s/1/
#container {
overflow: hidden;
}

margin-top not working with clear: both

<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?
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:
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:
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/
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>

Categories

Resources