Do block elements ignore floating elements? - html

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.

Related

What does a block formatting context actually do?

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.

what does "either it fits or there are no more floats present" mean in the W3C specs regarding float?

.wrapper {
width: 500px;
}
.image {
display: inline-block;
width: 50%;
height: 100px;
background: red;
}
.image1 {
float: left;
width: 50%;
height: 50px;
background: yellow;
}
.image2 {
float: left;
width: 50%;
height: 50px;
background: red;
}
<div class="wrapper">
<div class="image"></div>
<div class="image1"></div>
<div class="image2"></div>
</div>
https://jsfiddle.net/8akzqx3p/
What I originally thought was that image2 was just below image1. However, it is far below. I found this reason in the W3C specs, but I don't know what this means.
https://www.w3.org/TR/CSS21/visuren.html#floats
If there is not enough horizontal room for the float, it is shifted downward until either it fits or there are no more floats present.
But I don't know what it means, especially "either it fits or there are no more floats present"
To understand this, we need to take both the part you quoted and its preceding paragraph. Together they say:
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.
If there is not enough horizontal room for the float, it is shifted downward until either it fits or there are no more floats present.
Which means:
If there is a line box ...
Initially the first inline-block image causes a line box to exist which is the height of the image vertically aligned with the line box's strut. (So just slightly taller than the image alone). Because there are other elements inside the block formatting context (the floats) the line box will be wrapped in an anonymous block box.
So the first floated image just fits alongside, and its top is aligned with the top of that line box.
But there's no space for a second floated image that both can be aligned with the top of that line box and alongside the inline-block image. So this second floated image must be placed after that line box, and after the anonymous block box that wraps it. There is no second line box so
it is shifted downward until either it fits ...
We're in a block formatting context here, so after a block box, means immediately following it on the block axis. There it fits, so that's where it is placed.
... or there are no more floats present.
This is not in play in your example. It allows for the case where the width of the float is defined as having a width of, for example, 110% of its containing block. It'll never fit, but can be placed immediately below the point where there are no preceding floats alongside it.

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 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.

Baseline shifts for block-level element when no content inside

I'm working through a CSS problem at http://www.codecademy.com/courses/web-beginner-en-jNuXw/0/1?curriculum_id=50579fb998b470000202dc8b (actually, just helping a friend learn HTML/CSS) and came across a curious issue. If you erase the content in any of the <p> tags within a <div>, the div shifts upward. For example, delete the word 'Mom' without deleting the <p>. As best as I can figure out, this is because the element is set to vertical-align: baseline and for some reason the baseline is changing. I just can't figure out exactly why it's changing or what is causing it to change.
To be clear, I'm not asking for help to get the div's to align. That's simply a matter of setting their vertical-align to 'top'. I'm just trying to understand how the document flow is calculated. The specific question is: why does the empty div shift upwards?
DEMO: jsFiddle
UPDATE: Here is a new jsFiddle - http://jsfiddle.net/tonicboy/2DtTw/3/ which removes a lot of rules to boil the problem down to a simplified use case. From this, we can see that when a <p> tag has text in it, the baseline of the parent <div> is set at the baseline of the text. When you remove the text, the baseline of the parent <div> is set to the bottom of the <div>. Why is that?
HTML:
<div class="friend" id="best_friend"><p>Arthur</p></div>
<div class="friend"><p>Batmanuel</p></div>
<div class="friend"><p>Captain Liberty</p></div>
<div class="friend"><p>The City</p></div>
<div class="friend"><p>Justice</p></div>
<div class="family"><p></p></div>
<div class="family"><p>Dad</p></div>
<div class="family"><p>Bro</p></div>
<div class="family"><p>Sis</p></div>
<div class="family"><p>Rex</p></div>
<div class="enemy"><p>Baron Violent</p></div>
<div class="enemy"><p>The Breadmaster</p></div>
<div class="enemy"><p>The Deadly Nose</p></div>
<div class="enemy"><p>Dinosaur Neil</p></div>
<div class="enemy" id="archnemesis"><p>Chairface</p></div>
CSS:
div {
position: relative;
display: inline-block;
height: 100px;
width: 100px;
border-radius: 100%;
border: 2px solid black;
margin-left: 5px;
margin-top: 5px;
text-align: center;
}
div p {
position: relative;
margin-top: 40px;
font-size: 12px;
}
.friend {
border: 2px dashed green;
}
.family {
border: 2px dashed blue;
}
.enemy {
border: 2px dashed red;
}
#best_friend {
border: 4px solid #00C957;
}
#archnemesis {
border: 4px solid #cc0000;
}
I think I've mostly figured out the reason, after digging through W3C specs. Here are three key items from the spec which may explain this behavior:
"Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose."
When you delete the text, the <p> element is no longer in-flow.
"The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge."
Because there are no in-flow elements within the parent div, the baseline becomes the bottom margin.
Because the div's are set to display: inline-block, their default vertical alignment is 'baseline'
Because the other div's have in-flow elements (the <p> tags), their baseline is set to the text baseline.
And that is why the empty box's bottom margin aligns with the baseline of the <p> tags in the other div's.
The baseline of the element is shifting because the text inside the <p> determs the baseline height:
In an inline formatting context, boxes are laid out horizontally, one
after the other, beginning at the top of a containing block.
Horizontal margins, borders, and padding are respected between these
boxes. The boxes may be aligned vertically in different ways: their
bottoms or tops may be aligned, or the baselines of text within them
may be aligned.
source: http://www.w3.org/TR/CSS2/visuren.html#block-formatting
The height of each inline-level box in the line box is calculated. For
replaced elements, inline-block elements, and inline-table elements,
this is the height of their margin box; for inline boxes, this is
their 'line-height'.
source: http://www.w3.org/TR/CSS2/visudet.html#line-height
CSS assumes that every font has font metrics that specify a
characteristic height above the baseline and a depth below it. In this
section we use A to mean that height (for a given font at a given
size) and D the depth. We also define AD = A + D, the distance from
the top to the bottom.
source: http://www.w3.org/TR/CSS2/visudet.html#inline-box-height
So with this block being a inline-block and baseline is calculted based on the line-height which is calcuted by different font types. Because this <p> has no font/text the baseline will not be positioned.
place all the line-height: 0; and you will see that the one with no text/font doesn't react like the other does:
jsFiddle
So why are the other two elemets shifting that have text in them?
Well it's because the text excist of two lines of text. The margin of the text is bigger and uses more space, thus the baseline is pushed further