Block Formatting Contexts, Collapsing Margins and Floating Containers - html

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 cleared 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!

Related

Why does margin-left not work on block elements to the right of float:left images? [duplicate]

Here is my code:
.wrapper {
border: 1px dashed red;
overflow: auto;
/*clear floating*/
}
.left {
width: 120px;
border: 5px solid #ddd;
float: left;
margin-right: 20px;
}
.right {
border: 5px solid #ddd;
overflow: auto;
/*make div.right become a new BFC*/
}
<div class="wrapper" id="wrapper">
<div class="left">This is a float div, width 120px.</div>
<div class="right">This is a div.</div>
</div>
why cannot I use margin-left:20px; in .right to get the same result as margin-right:20px; in .left?
I know margin-left:150px in .right can get the same result, but why div.right as a new BFC still ignore the floating div.left while div.left doesn't ignore div.right?
margin-left:20px in .right
margin-right:20px in .left
The relevant part of the spec is https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float which says:
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3. CSS2 does not define when a UA may put said element next to the float or by how much said element may become narrower.
Which gives browsers a bit of latitude in what they actually do here, but the point is, if they place the BFC next to the float, the Border Box of the BFC must not overlap the Margin Box of the float. So changing the size of the margin on the BFC won't impact on that constraint, but changing the size of the margin on the float will.
The MDN doc says :
when you float the element to left the elements comes after taken out of the normal flow of the document (though still remaining part of it). It is shifted to the left, or right, until it touches the edge of its containing box, or another floated element.
so if the right element have width of 100px
the floated element comes after it will not take any action if you give it margin lower than 100px e.g 20px because it already far enough from this point but if you give it 200px it will take action
.wrapper {
border: 1px dashed red;
overflow: auto;
/*clear floating*/
}
.left {
width: 120px;
border: 5px solid #ddd;
float: left;
margin-right: 20px;
}
.wrapper:after {
content: "";
display: table;
clear: both;
}
.right {
border: 5px solid #ddd;
overflow: auto;
/*make div.right become a new BFC*/
margin-left:200px; 👈👈
}
<div class="wrapper" id="wrapper">
<div class="left"></div>
<div class="right"></div>
</div>

Why margin-left of a Block Formatting Context element cannot get a same result as margin-right of a float element?

Here is my code:
.wrapper {
border: 1px dashed red;
overflow: auto;
/*clear floating*/
}
.left {
width: 120px;
border: 5px solid #ddd;
float: left;
margin-right: 20px;
}
.right {
border: 5px solid #ddd;
overflow: auto;
/*make div.right become a new BFC*/
}
<div class="wrapper" id="wrapper">
<div class="left">This is a float div, width 120px.</div>
<div class="right">This is a div.</div>
</div>
why cannot I use margin-left:20px; in .right to get the same result as margin-right:20px; in .left?
I know margin-left:150px in .right can get the same result, but why div.right as a new BFC still ignore the floating div.left while div.left doesn't ignore div.right?
margin-left:20px in .right
margin-right:20px in .left
The relevant part of the spec is https://www.w3.org/TR/CSS22/visuren.html#bfc-next-to-float which says:
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3. CSS2 does not define when a UA may put said element next to the float or by how much said element may become narrower.
Which gives browsers a bit of latitude in what they actually do here, but the point is, if they place the BFC next to the float, the Border Box of the BFC must not overlap the Margin Box of the float. So changing the size of the margin on the BFC won't impact on that constraint, but changing the size of the margin on the float will.
The MDN doc says :
when you float the element to left the elements comes after taken out of the normal flow of the document (though still remaining part of it). It is shifted to the left, or right, until it touches the edge of its containing box, or another floated element.
so if the right element have width of 100px
the floated element comes after it will not take any action if you give it margin lower than 100px e.g 20px because it already far enough from this point but if you give it 200px it will take action
.wrapper {
border: 1px dashed red;
overflow: auto;
/*clear floating*/
}
.left {
width: 120px;
border: 5px solid #ddd;
float: left;
margin-right: 20px;
}
.wrapper:after {
content: "";
display: table;
clear: both;
}
.right {
border: 5px solid #ddd;
overflow: auto;
/*make div.right become a new BFC*/
margin-left:200px; 👈👈
}
<div class="wrapper" id="wrapper">
<div class="left"></div>
<div class="right"></div>
</div>

Firefox removes background when floating elements

I have a wrapper div in my css that covers most of my viewport. I have a background color and inside that div, I have two more divs floated right and left respectively.
When I'm with Chrome, I can see the background code perfectly, but when I'm with Firefox, the background color is not seen. Here are some screenshots:
On Chrome:
On Firefox
And here's the code.
My HTML:
<div id="wrapper">
<div id="asd"></div>
<div id="perejavi">
<p id="pere">Pere</p>
<p id="and">&</p>
<p id="javi">Javi</p>
</div>
<div id="web">
<p id="programmer">Programación</p>
<p id="programmer2">diseño web</p>
</div>
My CSS:
#wrapper {
background-color: #00CE6B;
height: auto;
min-height: 93%;
width: 85%;
color: #ffffff;
margin: 0 auto;
}
#perejavi {
margin: 0;
font-size: 9.1em;
float: left;
padding-bottom: 0;
height: 60%;
width: 50%;
}
#web {
margin: 0;
width: 50%;
height: 60%;
float: right;
}
Why is this happening? Hope you can help!;)
Floats remove the HTML element from the normal document flow and can cause issues like this. There are a couple ways to handle floats but i'll just give you the way I prefer to do it:
in your css:
.clearfix:after {
visibility:hidden;
display:block;
font-size:0;
content:" ";
clear:both;
height:0;
}
Then in your HTML:
<div id="wrapper" class="clearfix>
This creates a pseudo element as the last child of your .clearfix div which clears the floated elements contained within the .clearfix div
note: Also, you seem to be missing your closing </div> for your wrapper div. Make sure you add that in
Further reading on floats and the strangeness they bring with them:
http://css-tricks.com/all-about-floats/

Inline-block elements expanding space below

Creating a page layout using inline-block elements (vertically aligned to the top). The only issue, is that inline-block elements below another set of inline block elements will not fold into open space like floated elements do. It's almost as if it obeys row-like rules. Are there any fixes for this?
Layout example in JSFiddle
CSS
* {
font-family:helvetica;
font-size:18px;
}
.container {
margin:0 auto;
width:90vp;
}
.main_content {
background:red;
display:inline-block;
vertical-align:top;
box-sizing:border-box;
width:76.04%;
min-height:200px;
}
.content_details {
background:blue;
display:inline-block;
vertical-align:top;
box-sizing:border-box;
width:22.39%;
margin-left:01.56%;
min-height:250px;
}
.comments {
background:green;
display:inline-block;
vertical-align:top;
box-sizing:border-box;
width:76.04%;
min-height:150px;
}
HTML
<div class="container">
<div class="main_content">
<h1>Main Content</h1>
</div
><div class="content_details">
<h2>Details</h2>
</div
><div class="comments">
<h2>Comments</h2>
</div>
</div>
Please note I can change the mark-up to create only two inline-block elements (creating two columns), however I would like to know if there is a fix for 3 separate inline-block elements (like in the JSFiddle example), that way I wouldn't need to add extra mark-up.
No there isn't.. Not like you are talking about. You'd have to use:
<div id="col1">
<div id="maincontent"></div>
<div id="comments"></div>
</div>
<div id="details"></div>
Then you would have #col1 and #details as inline-block elements.
The whole point of an inline-block is that it is inline (i.e. on a line with other elements) it isn't acting like a table as you suggested, it's acting like a line of text (as it should) that is wider than it's container and breaking to the next line down.
See here: http://jsfiddle.net/GXmM6/ for a working example
Neither floats nor inline-block will do what you want there, unless you wrap each column in its own div. Short of that, there are JavaScript solutions for doing this, such as Masonry. (It involves a lot of positioning, though.)
Did I get it right that you wanted the .content_details to be a sidebar? Then I just changed it from display: inline-block to float: right to place .comments seamlessly beneath your .main-content. See http://jsfiddle.net/koivo/7UqqF/ for working example. Think that even works just with display: block ...
* {
font-family: helvetica;
color: white; /* added */
font-size: 18px;
}
.container {
margin: 0 auto;
width: 90vp;
}
.main_content {
background: red;
display: inline-block;
vertical-align: top;
box-sizing: border-box;
width: 76.04%;
min-height: 200px;
}
.content_details {
background: blue;
/* display: inline-block; */
float: right; /* added */
vertical-align: top;
box-sizing: border-box;
width: 22.39%;
margin-left: 01.56%;
min-height: 250px;
}
.comments {
background: green;
display: inline-block;
vertical-align: top;
box-sizing: border-box;
width: 76.04%;
min-height: 150px;
}

div does not get centered using margin: auto in IE9

I am trying to get a centered in the space that is left empty by a sidebar. This is how I'd like it to look like:
I actually managed to make this work OK for most browsers using margin: auto for the div in question, while setting overflow: hidden:
Fiddle here
CSS
#header {
height: 50px;
background: #224444;
color: #fff;
}
#container div {
padding: 1em;
}
#content {
max-width: 400px;
margin: auto;
background: #ddd;
height: 300px;
overflow: hidden;
}
#sidebar {
float: right;
width: 200px;
background: #aaa;
height: 300px;
}
HTML
<div id="container">
<div id="header">
PAGE HEADER
</div>
<div id="sidebar">
Sidebar
</div>
<div id="content">
Centered Content
(Works everywhere but on IE9)
</div>
</div>
However, it does not work with IE9. It is strange as IE8 works OK!
I am running out of ideas, so I thought that maybe someone knows what is going on? The trick seems to work perfectly everywhere else.
NOTE: Please note that the content div should be flexible as it is in the demo. As the available space decreases, it should change size and squeeze in.
Isolate the centering from the floating
This affects IE9/10.
It works fine if the floated element is removed, or if width is used instead of max-width. The presence of floated content, combined with the use of margin:auto and max-width instead of width, appears to be confusing IE9+.
To fix this, put the centered content in a wrapper div, so that the centering of the content can be separated from the floating of the sidebar. In other words, too much is happening layout-wise in a single div, more than IE9+ can handle. So split up the #content div into two separate divs.
#header {
height: 50px;
padding: 1em;
background: #224444;
color: #fff;
}
#content-wrapper {
overflow: hidden;
}
#content {
max-width: 400px;
margin: auto;
padding: 1em;
background: #ddd;
height: 300px;
}
#sidebar {
float: right;
width: 200px;
padding: 1em;
background: #aaa;
height: 300px;
}
<div id="container">
<div id="header">
PAGE HEADER
</div>
<div id="sidebar">
Sidebar
</div>
<div id="content-wrapper">
<div id="content">
Centered Content
</div>
</div>
</div>
This tested fine in IE7/8/9/10. On a side note, because a wrapper div was added, the padding: 1em; now has to be added to each element individually.
IE is notorious for not working without proper doctypes.
Try adding the HTML5 one
<!DOCTYPE html>
Floats are a tricky business. Strictly speaking, they're only supposed to affect the inline content that flows around them, so margins acts like the floats aren't even there.
Try this instead:
#container {text-align:center}
#content {display:inline-block;text-align:left}
This should make the content box act like an inline element, and therefore appear centered in the space.
As far as I remeber I've always problems with margin:0 auto because I didn't specify width property.
So everytime you want use margin:auto you propably should write this:
#content {
max-width: 400px;
margin: auto;
background: #ddd;
height: 300px;
overflow: hidden;
width:500px;
}
or in percentage:
#content {
max-width: 400px;
margin: auto;
background: #ddd;
height: 300px;
overflow: hidden;
width:30%;
}
EDIT
If you want to create flexible layout please take a look to bootstrap and fluid grids.