How does the CSS Block Formatting Context work? - css

How does the CSS Block Formatting Context work?
CSS2.1 specifications says that in a block formatting context, boxes are laid out vertically, starting at the top. This happens even if there are floated elements in the way, except if the block box established a new block formatting context. As we know, when browsers render block boxes in a block formatting context, the floated element is omitted, why does establishing a new block formatting context work?
How are boxes (block boxes and inline boxes) laid out in the normal flow?
I read somewhere that block elements generate block boxes, but floating elements are ignored when a user agent draws box and take them into account when they fill out content. Whilst floating elements will overlap other elements's boundary of the boxes, the solution is establishing a new block formatting context for the overlapped elements using overflow:hidden.
"New block formatting context is still block formatting", so when drawing a box, it will also treat the floating element as if it doesn't exit. Is that right or have I misunderstood "new block formatting context?"
Update:more questions
By saying "It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code."
I don't understand the meaning, I will provide an example, maybe you will understand me.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<h3>This is a content box</h3>
<p>It contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float. This is normal behaviour.</p>
<div class="float">floated box</div>
</div>
I thought the floating box should float to the top of the containg block-the div with class content. Besides, if the floating box appears earlier in the markup, then it will display what I think it should be.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
How can we explain this? Can we use "block formatting context and inline formatting context" to explain it?

Block Formatting Contexts
Floats, absolutely positioned
elements, block containers (such as
inline-blocks, table-cells, and
table-captions) that are not block
boxes, and block boxes with 'overflow'
other than 'visible' (except when that
value has been propagated to the
viewport) establish new block formatting contexts for their contents.
With my bold, it's the establish bit that is important
What this means is that the element you use overflow (anything other than visible) or float or inline-block..etc on becomes responsible for the layout of its child elements. It's the child elements which are then "contained", whether that's floats or collapsing margins they should be wholly contained by their bounding parent.
In a block formatting context, each
box's left outer edge touches the left
edge of the containing block (for
right-to-left formatting, right edges
touch)
What the above line means:
Because a box can only be rectangular and not irregular shaped this means a new block formatting context between two floats (or even beside one) will not wrap around neighbouring side floats. The inner, child boxes can only extend as far as to touch their parents left (or right in RTL) edge. It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code.
Float Clearance
In normal circumstances floats are supposed to clear all previous floated elements, that's previously floated in the whole source code, not just your displayed "column"
The telling quote from the "float clearance specs" is:
This property indicates which sides of
an element's box(es) may not be
adjacent to an earlier floating box.
The 'clear' property does not consider
floats inside the element itself or in other block formatting contexts
So say you have a three column layout where the left and right columns are floated left and right respectively, the side columns are now in new Block Formatting Contexts, because they are floated (remember float is also one of the properties that establish a new BFC), so you can happily float list elements inside them and they only clear floats which are already inside the side columns they no longer care about floats previously in the source code
##To make the main content a new Block Formatting Context or not?
Now that middle column, you can simply margin it from both sides so that it appears to sit neatly between the two side floated columns and take the remaining width, a common way to get desired width if the centre column is "fluid" - which will be fine until you need to use floats/clearance inside your content div (a common occurrence for those using "clearfix" hacks or templates including them)
Take this very simple code:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated">this a floated box</div>
</div>
It produces the following:
In general this is fine, especially if you have no background colours or internal (in the main content) floats - notice the floats are fine (not cleared yet) they're doing probably what you except them to but they, the H3's top margin and the p's bottom margin are not actually really contained by the content div (lightgrey background).
So to the same simple margined scenario of above code add:
.clear-r {clear: right;}
to the CSS, and change the second HTML floated box to:
<div class="floated clear-r"> this a floated cleared box</div>
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This time you get this:
The second float is clearing the right side but it's clearing the whole height of the right column. The right column is floated earlier in the source code so it's clearing it as told! Probably not the desired effect though, also note the h3 and p margins are still collapsed (not contained).
###Make it establish a Block Formatting Context, for the sake of the children!
and finally make the main content column take responsibility - become a Block Formatting Context - for its contents : remove margin: 0 200px; from the main content CSS and ADD overflow: hidden; and you get this:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
overflow: hidden;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This is probably much more like what you would expect to happen, note now the floats are contained, they clear properly ignoring the right side column, and also the h3 and p margins are contained instead of collapsed.
With the extensive use of resets these days the margins are less noticeable (and IE still doesn't get them quite right) however what just happened to the centre "main content" is that it became a Block Formatting Context and is now responsible for its own child (descendant) elements. It's actually very similar to Microsoft's early days notion of hasLayout, it uses the same properties display: inline-block, float, and overflow anything other than visible, and of course table cells always have layout.. it is however without the bugs ;)
##Update: re more information in question:
When you say "but floating elements are ignored when user agent draws box and take them into account when they fill out content."
Yes floats normally overlay their container boxes, is that what you mean about parent boundaries? When a block element is drawn and it contains a float the block parent itself is drawn as a rectangle under the float and it is the "inline anonymous boxes" or simply "line boxes" of the other child elements that are shortened to make room for the float
Take this code:
#content {
background: #eee;
color #000;
border: 3px solid #444;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: left;
margin: 10px;
}
p {
background: #444;
color: #fff;
}
<div id="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
Which produces this:
You see that the parent element doesn't actually contain the float, as in it doesn't wrap it entirely.. the float is simply floating on top of the content - if you were to keep adding content to the div it would eventually wrap underneath the float because there would be no need for the (anonymous) "line boxes" of the p element to shorten themselves any more.
I've coloured the paragraph element so you can see that it too actually goes under the float, the darkgray background is where the paragraph starts, the white text is where the "anonymous line box" starts - it's only actually them that "make room" for the float, unless you tell it otherwise (i.e. you change the context)
Again referring to the above picture, if you were to margin the left side of thep element, yes it will stop the text wrapping under the bottom of the float because the "line boxes" (the white text) will only touch the left edge of their container, and you will bring the coloured background of the p to the right, clear of the float, but you won't have changed the behaviour of the p's formatting context. Like the centre column in the first picture way above ;)

Related

In CSS, how does overflow interact with float?

I'm confused by the interaction I'm seeing between the overflow property on an element and float on a sibling element. Consider the following:
.div1 {
float: left;
width: 100px;
height: 50px;
margin: 10px;
border: 3px solid #73AD21;
}
.div2 {
border: 1px solid red;
width: 50px;
height: 150px;
}
<h2>Without clear</h2>
<div class="container">
<div class="div1">div1</div>
<div class="div2">div2 - Notice that div2 is after div1 in the HTML code. However, since div1 floats to the left, the text in div2 flows around div1.</div>
</div>
(This example was adapted from this example on w3schools: https://www.w3schools.com/css/tryit.asp?filename=trycss_layout_clear
In this case, dev1 floats to the left of div2, and is within the box for div2 — e.g., the border for div2 extends above and to the left of div1, but the text content of div2 wraps around div1. But also note: because of the width/height set on div2, the text in div2 overflows below.
Now, add overflow:hidden; to div2:
.div1 {
float: left;
width: 100px;
height: 50px;
margin: 10px;
border: 3px solid #73AD21;
}
.div2 {
border: 1px solid red;
width: 50px;
height: 150px;
overflow: hidden;
}
<h2>Without clear</h2>
<div class="container">
<div class="div1">div1</div>
<div class="div2">div2 - Notice that div2 is after div1 in the HTML code. However, since div1 floats to the left, the text in div2 flows around div1.</div>
</div>
Now div2 is entirely to the right of div1 — it's border no longer extends around div1.
Why does adding the overflow:hidden property to div2 change its layout interaction wtih div1 in this way? (Same effect also happens for overflow:auto or overflow:scroll.)
You need to consider the concept of Block formatting contexts where you can read the following:
Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
So when adding overflow:hidden, the div2 will establish a new BFC then we can read:
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 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 ref
To make it easy, when an element create a BFC its content will no more interact with the outside world. From the MDN you can read:
Setting overflow: auto created a new BFC containing the float. Our <div> now becomes a mini-layout inside our layout. Any child element will be contained inside it.

What does collapsing width height and margin mean for block level elements?

What does it mean that the width of block level elements can not be collapsed but the height can?
And can you please explain this text from the W3.org specification:
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.
The meaning of the word collapse is causing much of the confusion here.
A collapsed margin is the name given to the instance when margins of two different elements occupy the same space.
Consider the following example:
.box {
height: 50px;
width: 50px;
}
.box1 {
background: red;
margin-bottom: 25px;
}
.box2 {
background: blue;
margin-top: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
It's difficult to tell, but that whitespace between the two boxes is only 50px. You might think it should be 75px, because I've specified a margin-bottom of 25px on the top box, and a margin-top of 50px on the bottom box. 25 + 50 = 75, so why is the whitespace only 50px?
Well, margins can't have any content within them; a margin is specifically denoting a lack of content. Considering there is no content to display in a margin, the parser thinks they might as well be combined to optimise space.
The word 'collapsed' comes about because there are technically two different 'segments' of margins existing in the same place at the same time, 'collapsing' in on each other.
Note that this doesn't happen with margin-left and margin-right:
.box {
height: 50px;
width: 50px;
float: left;
}
.box1 {
background: red;
margin-right: 25px;
}
.box2 {
background: blue;
margin-left: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
The space above is indeed 75px. This can be a confusing concept to wrap your head around, but it's important to note that it only affects vertical margins. Further information about collapsing margins can be found at CSS Tricks and Mozilla.
It's also important to note that, by default, a block-level element takes up 100% of the width of its parent, but 0% of the height.
Here's an example illustrating this:
.parent {
background: blue;
border: 10px solid purple;
height: 50px;
width: 200px;
}
.child {
background: red;
}
<div class="parent">
<div class="child">Text</div>
</div>
In the above example, I specify both a width and a height on the parent, though I don't specify either on the child. As you can see, the child element inherits the 200px width, but does not inherit the 50px height.
Hopefully this helps clarify that a bit!

Uncollapse a vertical margin in two adjacent elements

There are multiple ways posted here to uncollapse a vertical PARENT margin, but nothing about uncollapsing vertical margins of adjacent elements. The only solution I found was in this answer (back in 2009):
<div style="overflow: hidden; height: 0px; width: 0px;"> </div>
Almost 7 years passed since there. Is some better way to do this (possibly using some CSS3)?
Basically, suppose you have: http://jsfiddle.net/ok2u3o3c/
<div class="one"></div>
<div class="two"></div>
div {
width: 300px;
height: 200px;
}
.one {
margin-bottom: 10px;
background-color: blue;
}
.two {
margin-top: 20px;
background-color: red;
}
What would be the most elegant way to make the distance between these 2 boxes 30px instead of 20px (where the first margin contributes 10px and doesn't collapses)?
Let's start with the relevant documentation explaining the behavior of collapsing margins:
8 Box model - 8.3.1 Collapsing margins
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.
The following rules apply, which means that there are a things that you can do to prevent the margins from collapsing for sibling elements:
Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children)
Therefore if you float the elements with collapsing margins, they will no longer collapse:
.collapsing-margins {
margin: 100px 0;
background: #f00;
float: left;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>
Margins of inline-block boxes do not collapse (not even with their in-flow children).
Therefore you could also add change the display of the elements to inline-block:
.collapsing-margins {
margin: 100px 0;
background: #f00;
display: inline-block;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>

float divs inside container, text leaks out the bottom of the container, I want it inside the container.

I have 2 divs inside a container, Each of the inside divs are floated to the left and right, I want to make 2 columns for data inside the main content div.
Currently, text leaks out the bottom of content-left and content-right, even though they are contained within content.
Here is a jsfiddle
<div id="content">
<div id="content-left">I want to put content in here<br/><br/><br/>This is outside? why? </div>
<div id="content-right">and more in here</div>
Maybe other content here, inside the content
</div>
And the CSS
#content-left{
width: 50%;
border: 1px dotted #aaa;
float: left;
}
#content-right{
width: 49%;
float: right;
border: 1px dotted #aaa;
}
#content{
background-color: #eee;
width: 95%; /* Width of Main Content Div, % for Fluid*/
height: auto;
max-width: 1350px; /*Max width, To wide on big monitor*/
margin: 0 auto;
padding: 10px;
padding-left: 20px;
padding-right: 20px;
}
Also, any tips about floating items would be great and might earn reps if I find them useful, I thought I had it mastered but obviously not! :P
You just need to add a clearing br inside the last div
Maybe other content here, but this should be below the 2 above divs in the rendered view.
<br style="clear:both;" />
</div>
http://jsfiddle.net/jasongennaro/sahbz/9/
This is happening because the floated elements are taken out of the document flow.
Currently, text leaks out the bottom of content-left and
content-right, even though they are contained within content.
You need to "clear/contain your floats", more information here: http://www.ejeliot.com/blog/59
One simple way to do that is to add overflow: hidden to #content.
See: http://jsfiddle.net/sahbz/8/
Pure CSS
.content:after {
clear: both;
content: '';
display: block;
}

trying to understand <div> element

<style>
div#float {
text-align: center;
float: left;
width: 150px;
height: 150px;
border: 1px solid blue;
background: gold;
}
div.content {
background: yellow;
border: 1px solid purple;
height: 150px;
}
</style>
<div id='float'>
Float text.
</div>
<div class='content'>
Content text.
</div>
<div class='content'>
Content text.
</div>
can anyone explain how the 2nd div content is on the newline ? while the 1st div is inline with div#float?
By default, divs will take up the entire width of the block. Your first one is just beginning after the floated div because that's where it can begin. Since both your content divs are block-level elements, they will take up the entire width available to them.
There are lots of ways to control this, but I'm not sure what you're after.
The second div is displayed as a block-element. Block elements have a width of 100%, so there is no room left for the first div to appear on the left size of your second div.
try switching the "display" css property to "inline-style"
The first <div class='content'> is 150px tall, the same as the #float one, so it's pushed down exactly one line. If you wanted the #float to "span" both lines, give it more height, e.g.:
div#float {
text-align: center;
float: left;
width: 150px;
height: 300px;
border: 1px solid blue;
background: gold;
}
You can try it out here.
As becomes clear with above answers, this is not an issue specific about divs. It's about something called "The flow" within html-pages.
DIV's render default as an block element, which gives it a maximum width. When you float such an element, it loses this effect. Block elements however do not automatically clear floats. Thus, the first contentdiv takes all space available next to the float. The other contentdiv starts at the newline.
It's behaving exactly as it should. The first div in the markup is floated and thus taken out of the flow. It'll take up the 150x150 slot up in the top left corner and anything that comes after it in the markup will try to squeeze in the left over space to the right of the floated div which is what the first content div does. div's are block level elements so they will take up the entire width of the available space. Unlike an inline element, they will not shrink to fit the rendered text.
The first content div will stretch out till the rightmost edge of the window. If you're wondering why the first div (float div) did not behave like a block level element, it's because it was floated, which will cause it to shrink to fit it's contents (in your case you gave it an explicit width of 150px).
Once the browser has rendered the float div and the content div, it has exhausted the left over space so it goes to the next line and starts from the very beginning of that line for the second content div.
Looks like you are trying to set up a navigation bar followed by content. If you wrap your content in a div and float that div to the left, you will stay to the right of the gold div but a column drop will occur when the browser is resized too small to accommodate them. So you need a parent div with a width for both the float and content divs. I added an 800 pixels outerwrapper div to your markup.
.....
div#contentwrapper {
float: left;
width: 550px;
}
#outerwrapper {
width: 800px;
}
</style>
</head>
<body>
<div id="outerwrapper">
<div id='float'>
Float text.
</div>
<div id="contentwrapper">
<div class='content'>
Content text.
</div>
<div class='content'>
Content text.
</div>
</div>
</div>
.....
You might also want to use a different name than the css reserved word "float" for your divs. For more CSS float tutorials.

Resources