In order to understand what does a block formatting context do, I'm trying to find out what's going on when a BFC is not created.
I took the following demo from Everything you Know about Clearfix is Wrong:
.wrapper {
width: 740px;
background: #cccccc;
}
.leftSidebar {
float: left;
width: 200px;
}
.rightSidebar {
float: right;
width: 200px;
}
.mainContent {
padding-right: 200px;
padding-left: 200px;
}
.floatMe {
float: left;
background: teal;
color: #fff;
}
<div class="wrapper">
<div class="leftSidebar">
<h2>Heading</h2>
<pre>.leftSidebar {
float:left;
width:200px;
}</pre>
</div>
<div class="rightSidebar">
<h2>Heading</h2>
<pre>.rightSidebar {
float:right;
width:200px;
}</pre>
</div>
<div class="mainContent">
<h2>Heading</h2>
<pre>.mainContent {
padding-right:200px;
padding-left:200px;
}</pre>
<div class="floatMe">
<pre>.floatMe {
float:left;
background:teal;
color:#fff;
}</pre>
</div>
</div>
</div>
According to that article(emphasis mine):
In modern browsers:
All elements belong to the same block formatting context so adjacent
margins collapse. The heading’s margin “sticks out” of the wrapper to
butt against the p
. Unlike in IE, it is that margin (not the one on
the black box) that creates the gap above the wrapper.
I cannot understand what does "the same block formatting context" refers to. I want to know why such a weird layout is produced without a block formatting context.
I've tried to figure out the exact layout by adding * {border: 1px solid blue;}
to CSS, but the overall layout changed greatly after this change: now it behaves as if wrapper
is a block formatting context!
.wrapper {
width: 740px;
background: #cccccc;
}
.leftSidebar {
float: left;
width: 200px;
}
.rightSidebar {
float: right;
width: 200px;
}
.mainContent {
padding-right: 200px;
padding-left: 200px;
}
.floatMe {
float: left;
background: teal;
color: #fff;
}
* {
border: 1px solid blue;
}
<div class="wrapper">
<div class="leftSidebar">
<h2>Heading</h2>
<pre>.leftSidebar {
float:left;
width:200px;
}</pre>
</div>
<div class="rightSidebar">
<h2>Heading</h2>
<pre>.rightSidebar {
float:right;
width:200px;
}</pre>
</div>
<div class="mainContent">
<h2>Heading</h2>
<pre>.mainContent {
padding-right:200px;
padding-left:200px;
}</pre>
<div class="floatMe">
<pre>.floatMe {
float:left;
background:teal;
color:#fff;
}</pre>
</div>
</div>
</div>
Please tell me what's going on.
Good question, got me thinking a lot!
There are lot of concepts at play here, so I'll get to them one by one:
Buggy IE:
Whatever is mentioned in this old article about IE can be easily ignored if you do not have to design for IE7 or IE8 compatibility mode. This behavior is due to hasLayout property used internally by IE7.
See this MSDN doc for IE7:
What is "HasLayout" and why is it important?
There are several bugs in
Internet Explorer that can be worked around by forcing "a layout" (an
IE internal data structure) on an element.
Clearly this is a non-standard workaround and along with brings up a lot of inconsistencies. Read about this here too.
Block Formatting Context (BFC):
Excerpts from this MDN doc:
A block formatting context is a part of a visual CSS rendering of a
Web page. It is the region in which the layout of block boxes occurs
and in which floats interact with each other.
BFCs are very important for positioning and clearing of floated elements- floated elements affects only within the same BFCs. When you float
an element, it is taken out of the flow and reinserted by "floating".
See the examples below:
The inside of wrapper
is a BFC where you float one div to left and another to the right.
The floated elements are reinserted into the BFC while rendering around the element that is not floated.
As you have not clear
ed the floating in the BFC, the wrapper
height will extend to the size of the element that is not floated.
body{
margin: 0;
}
*{
box-sizing: border-box;
}
.wrapper{
border: 1px solid;
}
.wrapper > * {
display: inline-block;
border: 1px solid red;
width: 33.33%;
height: 100px;
}
.left{
float: left;
}
.right{
float: right;
}
.center{
height: 50px;
}
<div class="wrapper">
<div class="left">Left</div>
<div class="center">Center</div>
<div class="right">Right</div>
</div>
See what happens when you clear
the floating in the BFC- now the heights will behave normally in the wrapper
BFC.
body{
margin: 0;
}
*{
box-sizing: border-box;
}
.wrapper{
border: 1px solid;
}
.wrapper > * {
display: inline-block;
border: 1px solid red;
width: 33.33%;
height: 100px;
}
.left{
float: left;
}
.right{
float: right;
}
.center{
height: 50px;
}
.wrapper:after{
content: '';
display: block;
clear: both;
}
<div class="wrapper">
<div class="left">Left</div>
<div class="center">Center</div>
<div class="right">Right</div>
</div>
Collapsing Margins:
Top and bottom margins of blocks are sometimes combined (collapsed)
into a single margin whose size is the largest of the margins combined
into it, a behavior known as margin collapsing.
Margins collapse for adjacent blocks, parent and first/last child and empty blocks. See more about margin collapsing in this MDN doc.
Also note that:
Margins of floating and absolutely positioned elements never collapse.
So what really happens here?
So now you will have understood about BFCs and also how floating containers work in first case (when you have no borders specified) - that's why floatMe
stays out of its immediate mainContent
wrapper and exactly why the height of wrapper
and mainContent
is as it looks there.
Layout and IE referred to are only in IE7 and is non-standard.
Everything else that happens is because of margin collapsing:
a. h2
and pre
margins collapse (adjacent siblings)
b. mainContent
shifts a little bit to the top to collapse with the margin on the body
(Parent and first/last child)
c. As wrapper
takes the height of mainContent
, the wrapper
height is also shifted upwards.
d. What happens when you apply borders is that the margin collapsing in (b) above is nullified! (see MDN doc above as to why)
Hope things are looking better now. Cheers!