Why Do Non-Floating Parents of Floating Elements Collapse

Why do non-floating parents of floating elements collapse?

Take a look at this section in the w3c CSS 2.1 spec: 9.5 Floats

Note this image at the bottom, just before section 3.5.1:

image protruding outside containing paragraph, while text in following paragraph wraps it.

... this seems to provide the use-case I'm after:

Case description: You have an image floated inside a paragraph with a great enough height that it extends well below its sibling text. In general, you'd want the text in the subsequent paragraph to wrap around this image as well. The way to achieve this is to allow the image to protrude outside the containing paragraph. Otherwise -- if the first, containing paragraph's height extends all the way down to wrap the image -- the subsequent paragraph gets pushed down completely below the image, leaving a large white-space between the texts of the two paragraphs.

However: more often than not we don't want this effect when using floats. So often we need floats to layout main areas of a web page (lest we resort to tables), and in these cases we typically need a container to expand to include whatever is inside in its height calculation.

My wish: It seems, then, that there should be a CSS property along the lines of:

box-model-height: [ include-floats | exclude-floats ];

Browsers could have the default on paragraphs as "exclude-floats", and all other blocks as "include-floats".

Or if that would break too many designs, "exclude-floats" could be the default everywhere, and we could still fix the situation, wherever we need to, entirely in the style-sheet, instead of requiring a class-name (e.g. clearfix) in the markup.

Floating elements causes the parent to collapse. HTML / CSS

Since you needed the reasons, I think this post explains it very well. Apart from reasons, it also has some solutions to tackle it.

About float:

 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 it's containing
box or another floated element.

Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/float

About clear:

The clear CSS property specifies whether an element can be next to floating 
elements that precede it or must be moved down (cleared) below them.

The clear property applies to both floating and non-floating elements.
When applied to non-floating blocks, it moves the border edge of the
element down until it is below the margin edge of all relevant floats.
This movement (when it happens) causes margin collapsing not to occur.

When applied to floating elements, it moves the margin edge of the element
below the margin edge of all relevant floats. This affects the position of later
floats, since later floats cannot be positioned higher than earlier ones.

The floats that are relevant to be cleared are the earlier floats within the
same block formatting context.

Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/clear

Why is this non-float margin collapsing with a float?

Never mind, I think I found it myself. Looks like the following assumption in my question was wrong (told you I don't fully grok the CSS float model):

The fact that the clearing element itself is not floated shouldn't be relevant.

In section 9.5.2, which describes the clear property, it says:

Computing the clearance of an element on which 'clear' is set is done by first determining the hypothetical position of the element's top border edge. This position is where the actual top border edge would have been if the element's 'clear' property had been 'none'.

If this hypothetical position of the element's top border edge is not past the relevant floats, then clearance is introduced, and margins collapse according to the rules in 8.3.1.

Then the amount of clearance is set to the greater of:

  1. The amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.
  2. The amount necessary to place the top border edge of the block at its hypothetical position.

And further down that section, it says:

When the property is set on floating elements, it results in a modification of the rules for positioning the float. An extra constraint (#10) is added:

  • The top outer edge of the float must be below the bottom outer edge of all earlier left-floating boxes (in the case of 'clear: left'), or all earlier right-floating boxes (in the case of 'clear: right'), or both ('clear: both').

(Emphasis mine. Note that "outer edge" is synonymous with "margin edge", as described in section 8.1.)

Essentially, this means if the clearing element is not floated and its top margin alone does not push it far enough away from the floats, then the element is pushed just enough for its top border to sit right underneath the bottom margin of the float being cleared. While it would appear as if its top margin was collapsing with the bottom margin of the float, in reality the top margin doesn't have any meaningful effect because it doesn't reach the border edge of the clearing element.

Otherwise if the clearing element is floated, then its top margin is taken into account, so to speak (as consistent with the rules stated in 8.3.1).

And as I write this, I recall that in-flow non-floating elements are positioned as if the floats were never there to begin with, because floats are taken out of the normal flow. In other words, any top margin that is set on a non-floating clearing element does not take into account any floats, regardless of whether it is enough for clearance. For example, when both clear and float are set to none on the last element, it sits flush with the edges of its container in the exact same position as the first floating element, albeit behind it (note that the borders on the container block margin collapse between it and the container).

Lastly, the fact that clearance is introduced is actually not relevant in this specific situation, because clearance blocks margin collapse only when the element's margins would normally have collapsed had its clear property been set to none. Since we're talking about floats here, margin collapse should indeed never happen normally, and so whether or not the last element has clearance isn't relevant (not directly, anyway).

floating elements behavior

This is the logical behavior of how the elements should be painted but you are having an overflow issue combined with how float works that is making things strange.

Let's remove some properties and follow the code step by step. Let's start by removing overflow:auto from main and the fixed height from .second

.main {  border-style: solid;  border-color: yellow; /* overflow: auto;*/}
.first { width: 200px; height: 100px; float: left; border: 10px solid green;}
.second { width: 200px; /*height: 50px;*/ border: 10px solid blue;}
<div class="main">  <div class="first">test1</div>  <div class="second">test2</div></div>

why this floating div do not goes into its parent element?

Swap the order of the left and float_r elements. Divs are block elements.

See fiddle: http://jsfiddle.net/hL8tvet8/4/

<div class="header">
<div class="float_R"></div>
<div class="left"></div>
</div>

Div collapse when using float

You have to "clear" your floats. Floating elements takes them out of the normal block positioning of the page, so an element that is floated right breaks out of the parent container, collapsing the div. You can clear them, or the more concise and clever way is to add 'overflow: auto' to the parent container:

#actions
{
background: blue; overflow: auto;
}

#buttons
{
float: left;
overflow: hidden;
}

#text
{
float: right;
overflow: hidden;
}

Since "overflow: auto" can produce scrollbars in certain instances, I usually explicitly prevent that by specifying 'overflow: hidden' on children elements. This works reliably in my experience.

Inconsistent alignment of a Floating Element

  1. A floating box will be placed vertically level with where the box would have been if it hadn't been floated. In the case here, where it is the first element in its container, its top edge is coincident with the top of the content box of its container. (The difference in effect you're seeing is the div container's content box moving up and down.)

  2. In the initial case, p1 is floated and therefore out of flow so there is nothing to stop the vertical margin of p2 from collapsing with its ancestors. The presence of a non-0 width border top means that the top margin of p2 is no longer adjacent to the top margin of the div element (i.e. they are separated by the border), so the margins can not collapse together.

  3. A side effect of the overflow:auto is that it causes the div to establish a block formatting context. The margins of descendants of a block formatting context won't collapse with the margins of the element that establishes the block formatting context. A more direct, but newer way to cause the div to establish a block formatting context is to give it a setting of display:flow-root.

  4. By default, adjacent vertical margins of in-flow block boxes collapse. So the default top margin of the p element (p2) collapses with the top margins of the div and body elements, increasing the margin above the top of the content box of the div element, and hence pushing the floated element lower.

  5. Collapsing Margins affects vertical margins. With horizontal margins, what you are seeing is not margin collapse, but a different effect. p1 and p2 overlap one another. p2 doesn't exactly wrap around p1, its content does. What happens is that p2 is made up of a stack of line boxes. The left edge of each line box which is alongside p1 is coincident with p1's right margin edge. p2's left margin still touches the left edge of the containing div's content box, exactly as it p1 wasn't there at all, so it won't affect the position of left edge of those line boxes unless p2's left margin, border and padding is greater than the width of p1's margin box.

Floating elements within a div, floats outside of div. Why?

The easiest is to put overflow:hidden on the parent div and don't specify a height:

#parent { overflow: hidden }

Another way is to also float the parent div:

#parent { float: left; width: 100% }

Another way uses a clear element:

<div class="parent">
<img class="floated_child" src="..." />
<span class="clear"></span>
</div>

CSS

span.clear { clear: left; display: block; }


Related Topics



Leave a reply



Submit