What's the containing block of floated elements? - html

In css2.1 spec, it says:
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.
I wonder what's the containing block of floated elements, and I test:
body {
position: relative;
margin: 5px;
padding: 10px;
}
p {
float: left;
position: absolute;
margin: 10px;
}
<body>
<p>hehe</p>
</body>
According to css2.1 spec, the absolute positioned element's containing block is the nearest positioned block container's padding box.
But in the code above, the floated element is floated to the content box boundary. I'm confused how to find the containing block of the floated element.

The premise of your question is flawed. An absolutely positioned element cannot float, and a float cannot be absolutely positioned. From section 9.7:
[...] if 'position' has the value 'absolute' or 'fixed', the box is absolutely positioned, the computed value of 'float' is 'none' [...]
So you're not trying to find the containing block of a float here. You're trying to find the containing block of an absposed element.
Having said that, if you really must know, the containing block of a float is the same as for relatively positioned or non-positioned elements as I described in my answer to your previous question, since floats cannot be absposed.

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.

Does position absolute make that element a containing block?

In css2.1 spec, w3.org, there is a example explain the way how containing blocks are formed.
<P id="p2">This is text
<EM id="em1">
in the
<STRONG id="strong1">second</STRONG>
paragraph.
</EM>
</P>
when position em as static, strong's containing block is established by p, but when position em as absolute, strong's containing block is established by em.
I look through the chapter about containing block and can't find whether absolute position will form a new containing block or not. Is there something that im missing?
We establish that #strong1 is a non-positioned inline box. Therefore, from the spec,
[...] if the element's position is 'relative' or 'static', the containing block is formed by the content edge of the nearest block container ancestor box.
When #em1 is not absolutely positioned, it remains an inline box. The nearest block container ancestor box to #strong1 is #p2, therefore #p2 is its containing block.
When #em1 is absolutely positioned, it turns into a block box as shown in section 9.7. This makes it the nearest block container ancestor box to #strong1, therefore #em1 becomes its containing block. A block box is defined as a block-level block container box.
So does absolute positioning cause an element to establish a containing block for relatively positioned or non-positioned boxes? Yes, but only when absolute positioning results in the element's box becoming the nearest block container ancestor of those boxes.
Note that this is a rather simplified case, since the only boxes in #em1 are inline boxes (including the two anonymous inlines surrounding #strong1). Besides the fact that not all block-level boxes are block containers (tables being a common example of a block-level box that's not a block container box), even if absolute positioning does result in an element generating a block box, since we're talking about an inline box here, it may very well be that the inline box's block container is an anonymous block box within the absolutely positioned element, if that element happens to contain a mix of both block-level and inline-level boxes. This complicated case is detailed in section 9.2.1.1.
But the complications don't stop there. The reason I say "it may very well be" is because whether or not an anonymous block box is capable of establishing a containing block isn't defined in CSS2.1.
If I understand your question correctly, yes em with absolute positioning will become the new containing block. Try changing the properties in this demo:
p{
//position: absolute;
overflow: hidden;
}
em{
position: absolute;
overflow: hidden;
background: yellow;
padding: 10px;
top: 10px;
}
strong{
padding: 20px;
background: red;
}
https://jsfiddle.net/xvrjve6s/

What does "position: relative" actually mean?

w3.org/TR/CSS2 says:
For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the content edge of the nearest block container ancestor box.
What does it mean? I read it as position: relative doesn't affect an element itself, but actually affects its parent - the containing block.
So, to change CSS of the containing block do I have to apply position to its child?
Isn't it strange? There is a pattern when you apply position: relative to a parent, and position: absolute to some of its child to make that absolutely positioned child be tied to a parent.
...actually affects its parent - the containing block.
The point of
... if the element's position is 'relative' or 'static', the containing
block is formed by the content edge of the nearest block container
ancestor box.
is to explain that its parent is not necessarily its containing block.
Most notably, if it is position:relative and its parent is display:inline, then its parent is not its containing block, but an element further up the DOM tree is instead.
position: relative means that the element which has been given a relative position will adjust itself relative to itself. For example:
An h1 is given the following CSS:
h1 {
position: relative;
top: 20px;
left: 20px;
}
then it will move 20px top and left from its original position. Check my fiddle: https://jsfiddle.net/w6sowku5/

Do floated elements ignore padding?

This answer states that:
When you float an element, it's effectively taking it out of the document flow, so adding padding to its parent won't have an effect on it. [...]
Also MDN states that:
[...] when an element is floated it is taken out of the normal flow of the document. It is shifted to the left or right until it touches the edge of its containing box or another floated element.
Well, somehow, I added padding to parent element and the floated one was shifted:
#a{
width: 100px;
height: 100px;
background-color: red;
float: left;
}
#parent
{
padding: 100px;
}
<!DOCTYPE html>
<body>
<div id=parent>
<div id=a></div>
</div>
</body>
</html>
No. Floats do not ignore the padding of their container.
The containing block of a float is established by the content edge of the container:
10.1 Definition of "containing block"
the containing block is formed by the content edge of the nearest
block container ancestor box.
That content edge is affected by the padding of the container:
8.1 Box dimensions
And floats can't go to the top or to the left than their containing block.
Float rules
The left outer edge of a left-floating box may not be to the left of the left edge of its containing block. An analogous rule holds for right-floating elements.
A floating box's outer top may not be higher than the top of its containing block.

CSS position:fixed without top gives unexpected layout?

Given:
<body>
<div id="fixed">
Fixed div
</div>
<div id="nonfixed">
<p>Non-fixed div</p>
<p>Non-fixed div</p>
<p>Non-fixed div</p>
</div>
</body>
And:
* { box-sizing: border-box; }
body {
margin: 0;
padding: 0;
}
#fixed {
position: static;
width: 100%;
border: 3px solid #f00;
}
#nonfixed {
margin-top: 50px;
border: 3px solid #00f;
}
Note that position:static, this gives the expected result (fiddle):
However, change position:static to fixed, and you get this (fiddle)
Even though the #fixed div is not inside #nonfixed, it has taken on the top margin of #nonfixed. This happens in both Chrome and Firefox. Curiously, the dev tools in both browsers do not show the #fixed div having any margins, so clearly it's being positioned as if it was fixed inside the #nonfixed div.
If I add top:0 to the #fixed ruleset the div goes back to the top of the window, but shouldn't this appear at the top (i.e. where it would in normal flow, but without affecting other elements) in the absence of a top specification?
For completeness: position:relative produces the same result as static and absolute looks the same as fixed.
I cannot find anything in the spec that directly addresses why an absolutely positioned element should be positioned relative to a subsequent sibling. In fact, reading the spec I find (emphasis mine):
10.6.4 Absolutely positioned, non-replaced elements
...
If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
...
'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
This seems to indicate the #fixed box should indeed be at the top of the viewport.
Since both FF and Chrome do the same thing I'm guessing it's supposed to work this way, but I'd like to know why. Can anyone explain this behavior in terms of the spec?
You'll notice that the "fixed" div is actually at the top of the body, the position and size of which match those of the "nonfixed" div.
This is most certainly due to the top margins of the body and div#nonfixed collapsing. See http://www.w3.org/TR/CSS21/box.html#collapsing-margins
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.
(...)
Two margins are adjoining if and only if:
both belong to in-flow block-level boxes that participate in the same
block formatting context
no line boxes, no clearance, no padding and
no border separate them (Note that certain zero-height line boxes (see
9.4.2) are ignored for this purpose.)
both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
top margin of a box and top margin of its first in-flow
(...)
The topis relative to the containing block, which is apparently not bodybut html (the root element).