What does a block formatting context actually do? - html

To preface, I understand how to create a BFC, I am confused on what a BFC actually does.
MDN says that
Elements participating in a BFC use the rules outlined by the CSS Box Model
and the spec says that
In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
MDN gives the example of a floated div that is out of flow, its height independent from the box parent.
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
/* overflow: hidden; */
}
.float {
float: left;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
And then having the box container ending up filling the height of the floated div. once overflow:hidden is declared which creates a BFC.
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
overflow: hidden;
}
How does participating in a BFC end up with this outcome? My thought process:
According to MDN's definition, participating in a BFC makes an element adhere to the box model...but don't all elements, even a floated element, adhere to the box model?
According to the spec's definition, all boxes are laid out vertically, but my-floated div and the p tag are side by side.
It seems that a BFC just makes the container fit all elements participating in its BFC, but if this is the case, then how come using a position: absolute instead of a float: left doesn't provide the same outcome of making the container fit the absolutely positioned element?
e.g.
<div class="box">
<div class="abs">I am an absolutely positioned box!</div>
<p>I am content inside the container.</p>
</div>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
overflow: auto;
position: relative;
height: 50px;
}
.abs {
position: absolute;
top: 250px;
left: 100px;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
Could someone explain what the BFC actually does?

Formatting contexts in general establish a set of rules for the layout of their contents.
The rules for Block formatting contexts are that their in-flow contents are laid out vertically one after the other.
In addition, BFCs have a few side-effects, that exist largely to make the rules established for their contents implementable, such as the way they work in the presence of floats inside or next to them.
Taking your points one by one,
According to MDN's definition, participating in a BFC makes an element adhere to the box model...but don't all elements, even a
floated element, adhere to the box model?
Yes they do. MDN does not actual say that it makes them adhere, just that they do adhere. The MDN statement is rather redundant, but not harmful or incorrect.
According to the spec's definition, all boxes are laid out vertically, but my right-floating div and the p tag are side by side.
This is incorrect. The floating div overlaps the p element. Only the inline content of the p element is side by side with the floating div.
It seems that a BFC just makes the container fit all elements participating in its BFC, but if this is the case, then how come
using a position: absolute instead of a float: left doesn't provide
the same outcome of making the container fit the absolutely
positioned element?
As said above, that BFCs contain their floats is best regarded as a side-effect of the BFC, not really what it does.
Finally, although BFCs provide some isolation of their contents from the outside world, it is not complete. For example, inline-block elements establish BFCs for their contents, but their baselines escape this isolation and are used to vertically align the elements with their inline neighbours.

A BFC is a kind of "isolation" to avoid any interaction of the inside and the outside world of an element. From another part of the MDN you can read:
because an element that establishes a new block formatting context will:
contain internal floats.
exclude external floats.
suppress margin collapsing.
Most of the observation you made (box model, absolute, boxes) aren't related to BFC. Just consider the creation of BFC as a way to encapsulate elements inside (mainly float elements because all the other elements are by default "inside"). Of course, this doesn't apply to absolute/fixed elements because they have their own rule that make them out of the flow.

Related

What's the logic behind the "new block formatting context" solution to the "I want to these floats to be wrapped by their parent" pattern?

I've been having a really hard time figuring out exactly what a Block Formatting Context is.
I've read the CSS specs but it just doesn't make sense to me.
So if given this classic problem
.container {
background-color: green;
border: solid 1px black;
}
.container div {
float: left;
width: 20%;
background-color: lightgreen;
}
<body>
<div class="container">
<div>Sibling</div>
<div>Sibling</div>
</div>
</body>
I've learned to solve it by creating a new Block Formatting Context within .container. This way, the .container div will expand and wrap the siblings floated elements.
.container {
background-color: green;
border: solid 1px black;
overflow: hidden;
}
.container div {
float: left;
width: 20%;
background-color: lightgreen;
}
<body>
<div class="container">
<div>Sibling</div>
<div>Sibling</div>
</div>
</body>
My understanding would be the following :
The root element of my html document (here <body>) creates a root block formatting context.
All subsequent positionned boxes participate in this root block formatting context (given they are not floated or absolutely positionned) and are hereby positionned accordingly to the Block Formatting Context Rules.
This is the normal flow positioning.
Adding float:left to my siblings elements get them out of the normal flow and act accordingly to other rules defined in the float property section of the specs.
Then, adding overflow:hidden to my .container div creates a new block formatting context that wraps the float. Because when a block box establishes a new Block Formatting Context it becomes the reference to which the children are positioned.
Now, this is my question :
Why are the float gotten out of the root block formatting context but are wrapped in my .container block formatting context? Why aren't they out of the flow entirely?
What's the difference between the root block formatting context and the new block formatting context established by the .container div?
The phrase "a box's containing block" means "the containing block in which the box lives," not the one it generates. Visual Formatting Model w3.org
In other words, the floated divs shown have a containing block of the .container div element, and inside of that block, they have been removed from the normal flow.
The next part is a little trickier, and I am not sure why this was done, but here is what was done. Although a div without a width definition can never overflow (it's width will always be 100% of its content width), using overflow:hidden when there is a float involved has some silent implications.
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. - Floats w3.org
The result of the "must not overlap" rule here is that the containing box will have to have its height adjusted in an edge case of a "block-level, non-replaced element in the normal flow".
In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge, then the height is increased to include those edges. Only floats that participate in this block formatting context are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not. - 'Auto' heights for block formatting context roots w3.org
Based on that, the height of the .container div is extended to cover the floated nested div elements and exhibits the difference you are seeing.

Do block elements ignore floating elements?

W3C states that:
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. However, the current and subsequent line boxes created next to the float are shortened as necessary to make room for the margin box of the float.
This work as expected with divs:
#a
{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
#b
{
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
background-color: red;
}
<body>
<div id=a></div>
<div id=b></div>
</body>
Here the red div is block-level element, therefore it's ignoring the floating one. (if I changed red one to display: inline-block it would appear next to floating one)
But, if I apply display: block to an image ,it won't ignore the floating div, Why?
#a
{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
#b
{
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
}
<body>
<div id=a></div>
<img id=b src="https://www.gravatar.com/avatar/5cb44dcd4ebddfea3ede8c6d46b02795?s=328&d=identicon&r=PG&f=1">
</body>
Several paragraphs after the one you quote, the spec 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.
Although you've applied display: block to your image, being an image it's a replaced element, and therefore the float cannot intrude into its bounds.
Only non-replaced block boxes that don't establish block formatting contexts and are in the same flow as a float may ignore the float.1 A block-level replaced element is not a block box, because a replaced element cannot be a block container box.2
1 You're probably thinking, that's a ridiculously specific statement, and it is. It's why terms like "block element" are considered extremely vague in CSS parlance. Then again, it doesn't help that CSS itself defines almost equally vague terms like "block box" to specifically refer to boxes that are both block-level boxes and block container boxes.
2 This does imply that "non-replaced block box" is somewhat of a tautology, which reinforces just how ridiculously specific that statement is.
W3C
These are the two things that stuck out to me when viewing the W3C. It's considering it as a line box.
A floated box is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float. If there is a line box, the outer top of the floated box is aligned with the top of the current line box.
A line box is next to a float when there exists a vertical position that satisfies all of these four conditions: (a) at or below the top of the line box, (b) at or above the bottom of the line box, (c) below the top margin edge of the float, and (d) above the bottom margin edge of the float.

Basic CSS floating elements behavior

I have troubles to understand some very basic css floating rules.
Let's say I have a HTML structure like this:
* {
padding: 0;
margin: 0;
}
div {
width: 200px;
height: 100px;
}
.base {
float: left;
border: 10px solid green;
}
.regular {
height: 50px;
border: 10px solid blue;
}
p {
border: 10px solid red;
}
<div class="base"></div>
<div class="regular"></div>
<p>Some text</p>
Now I don't understand two things:
Why are those two floating elements (div.regular and p) aligned to the left edge of the floated div.base element? I would expect they will be aligned to the right edge so they will be next to the base div element.
Why is the div.base element above all other elements? I would expect it at the bottom since it is before them in the HTML flow.
I would appreciate if you put some light on this for me or give me some resources to understand it.
Both of your questions can be answered by referring to section 9 of the CSS2.1 spec.
Why are those two floating elements (div.regular and p) aligned to the left edge of the floated div.base element? I would expect they will be aligned to the right edge so they will be next to the base div element.
From section 9.5:
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. However, the current and subsequent line boxes created next to the float are shortened as necessary to make room for the margin box of the float.
The line boxes refer to the boxes that actually contain the text. In your example, the p element contains one or more line boxes with the words "Some text". Those line boxes are made to reflow around the float. The block box that is generated by the p itself (which in turn contains those line boxes) is laid out as described in the first sentence, simply because block boxes obey different layout rules from line boxes.
Why is the div.base element above all other elements? I would expect it at the bottom since it is before them in the HTML flow.
From section 9.9, emphasis mine:
Within each stacking context, the following layers are painted in back-to-front order:
the background and borders of the element forming the stacking context.
the child stacking contexts with negative stack levels (most negative first).
the in-flow, non-inline-level, non-positioned descendants.
the non-positioned floats.
the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
the child stacking contexts with positive stack levels (least positive first).
Item #3 refers to div.regular and p, since they are in the normal flow, they are block-level, and they are non-positioned. #4 refers to div.base, which is floating.
Elements are painted in source order when
you have multiple elements belonging to the same category, for example when you have two floating elements, or
otherwise not mentioned in this list.
If you want to use float you should have a container, after that you should play with position of elements in css file:
.container{
width: 100%px;
height: 100%px;
border-style: dotted;
}
.base {
border: 10px solid green;
}
.regular {
height: 50px;
border: 10px solid blue;
}
p {
border: 10px solid red;
}
<div class="container">
<div class="base">div1</div>
<div class="regular">div2</div>
<p>Some text</p>
</div>

How to deal with elements that only contain absolutely positioned children?

I have a box that contains 2 absolutely positioned header and paragraph elements, I am experimenting on ways on how to center text and I tried tinkering using the method from this site.
Now the issue here isn't about ways on how to position text, because that's not my problem so you should not address that.
My problem is this.
How to make the parent element contain it's children?
Since the parent element contains elements that are absolutely positioned, that means the children are not in the flow and in turn the parent collapses.
Now this is a shocker, because I discovered that the containing/wrapping methods used in the same way for floats DO NOT WORK AT ALL.
Clearfix method.
Clearing div method
Overflow method.
They do not work at all!
The only method that decently works is the "Explicit Height" method which is very inefficient, unpredictable (might break on smaller windows) and not fluid.
Here is a simple, fixed-width rounded corner box who's "h1" and "p" elements I'd like to center using the above referred method.
Sample CSS,
body {
margin: 8px;
padding: 0;}
.box {
margin: 0;
padding: 0;
width: 335px;
position: relative;
background: #40331A url(bottom.gif) no-repeat left bottom;}
.inner {
background: url(top.gif) no-repeat left top;}
.box h2 {
width: 180px;
height: 27px;
top: 50%;}
.box p {
width: 120px;
height: 20px;
top: 50%;}
.box h2, .box p {
margin: 0;
position: absolute;}
Sample markup,
<div class="box">
<div class="inner">
<h2>This is a heading.</h2>
<p>This is a paragraph.</p>
</div>
</div>
So my question.
Is there any simple way to make a parent element contain its absolutely positioned children?
The entire point of absolutely-positioned elements is that the parent of those elements should be laid out as if those elements were never there. If you want the layout of your parent element to take into account its children, don't position them absolutely.
Since you're not including any relevant properties other than position: absolute, it seems completely superfluous, given that absolutely-positioned elements default to their static positions anyway. Just remove that declaration. It doesn't get any simpler than that.
The reason none of those methods work is because they are designed for floats, and absolutely-positioned elements are not floats:
You can't really "clear" anything that isn't a float.
Ditto.
The reason setting overflow works for floats is due to a consequence of the way floats participate in block layout, which does not apply to absolutely-positioned elements. What exactly this entails falls outside the scope of this question, but read up on block formatting contexts.

How the block element works inside a floated div?

This is a continuation of the following question:
CSS Float - The content stays in the default stack position
When I am adding a block element(p element) inside box1, (which comes after the floated box0), the p element wraps as expected. However, it is not starting from the edge of the container. Please see the image.
Also, the behaviour is different when I add display: inline-block, or float:left, or overflow:hidden to the paragraph element. Can someone please explain these scenarios?
http://jsfiddle.net/jintoppy/xGm5E/3/
<div class="box0">Box 0</div>
<div class="box1">Box1 should wrap with text elements.
<p>But, what about block elements? Why it is behaving like this</p>
</div>
.box0 {
width: 100px;
height: 100px;
background-color: green;
float: left;
opacity: .5;
}
.box1 {
width: 200px;
height: 100px;
background-color: red;
}
p {
border: 1px solid #000;
}
There isn't enough room for the text in the p to start a new line there, so it starts only at the right edge of the float, giving the appearance of a gap underneath the float. If you give the p a slightly bigger top margin, there will be enough room for the second line of text to start directly underneath the float:
p {
margin-top: 1.2em;
border: 1px solid #000;
}
You can also increase the line-height as mentioned by others; this will cause the first line to be tall enough for the second line to have room to start underneath the float.
The reason why the boundaries of the p element don't shift completely out of the float is because the p element behaves exactly like .box1, since neither of them are floating. The text wraps in the same way I've described in my previous answer.
The reason why it behaves differently when you add display: inline-block, float: left or overflow: hidden is given in the comments on my previous answer:
overflow: hidden causes the box to generate a block-formatting context, which makes it immune to surrounding floats. That causes the entire box to shift outside of the float and sit right next to it instead.
(Technically, display: inline-block also causes the p element to act like an inline element itself, in other words, it is rendered inline just like the first sentence in .box1, but this has the same effect.)
When neither .box1 nor p are generating block formatting contexts, they are both participating in the same context, which is actually that of the root element. This is what causes them to interact with the float the way you are observing.
Block formatting contexts are a very complicated and broad topic that I would rather not get into myself, but the answer to this question has a nice visual explanation on how they work and how elements interact with one another around them in various scenarios.
I would assume this is to do with the line height.
p {
border: 1px solid #000;
line-height:1.5;
}
This works :)
add overflow: hidden; in box1 class.Try this once.