HTML:
<div id="float_left">
DIV1
</div>
<div id="without_overflow">
DIV2
</div>
css:
#float_left{
float: left;
width:200px;
background-color: red;
}
#without_overflow{
width:400px;
height:40px;
background-color:green;
}
http://jsfiddle.net/kgypo14y/1
The result of the above code is what I expected. However, if I added overflow:auto or overflow:hidden to the second div the result is totally unexpected to me.
http://jsfiddle.net/60nzadLz/2/
Do you have a good explanation for that?
Thank you
Those values of overflow cause the element to establish a new block formatting context. A float may never intrude another block formatting context, so the entire element is shifted away from the float. From the spec:
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. 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.
Related
I am reviewing the floats property which i learned before,I found a simple issue about floated elements with its own stacking order, the code as:
Example 1:
.box-1{
background: teal;
width:100px; height:100px;
float: left;
}
<div class="box box-1"></div>
<p> this is the text for the testing purpose<p>
I totally understand the text will wrap around the the box which is right next to the box-1, but when there is no text elements only two div boxes:
Example 2:
.box {
width:100px;
height:100px;
}
.box-1{
background:teal;
float:left;
}
.box-2{
background:blue;
}
<div class="box box-1"></div>
<div class="box box-2"></div>
This time the .box-1 will overlap the .box-2 since it was floated and taken from the normal document flow.
So my questions are:
Since the p tag is a block element and it could be considered as a box. but why in the example 2 the p tag is moving to the right after the box-1? but in the example 1 there is totally different behavior?
It is because of the floated elements has same stack order like p tags and both of them have higher stacking order than the non-floated box as .box-2 here?
I am going to add more explanation as I think the accepted answer omitted some important parts and didn't provide a real explanation. Let's start with the definition of float from the MDN documentation:
The float CSS property specifies that an element should be placed
along the left or right side of its container, allowing text and
inline elements to wrap around it. The element is removed from the
normal flow of the web page, though still remaining a part of the flow
(in contrast to absolute positioning).
So yes float behave like absolute positioning but not exactly because element is still a part of the flow.
Now both of your examples behave exactly the same, the only difference is that in the first one you have text. So the float element doesn't push the p like you think but overlap it and push only the text. If you inspect the element you will see this:
So p is a block element and behave exactly like the box-2 in your second example and the floated element box-1 is above it. This confirms that in both examples we have the same thing but in the first one we have text inside the block element p and unlike absolute positioned element, floated element pushs text as described above.
Now why the floated element is above the p tag and above the box-2?
You can find this answer within the specificaton of the painting order. Both elements are not positioned and one is floated:
For all its in-flow, non-positioned, block-level descendants in tree
order: If the element is a block, list-item, or other block
equivalent:
All non-positioned floating descendants, in tree order.
As we can see, we first draw the in-flow element in the step (4) (in your case the p tag and the box-2) then we print the floating ones in the step (5) (the box-1).
To avoid such things you have two solutions (like provided in other answers):
You clear float which is a common solution used in order to avoid element being affected by the floating behavior.
You make the box-2 an inline-block element because inline-block behave like inline-elements and they are also pushed by floated element
I believe I understand the issue now (somewhat). Because they have the same dimensions, and because float: left kind of acts like display: absolute while maintaining text space, it's pushed box-2's text to the bottom.
You can get around this setting display: inline-block for box-2 and interestingly enough, putting an overflow: hidden or overflow: auto also fixes it.
.box {
width:100px;
height:100px;
}
.box-1{
float:left;
}
.box-2{
background:blue;
overflow: auto
}
<div class="box box-1">box-1</div>
<div class="box box-2">box-2</div>
Try this. Just add the overflow:hidden in your css for .box class.
.box {
width:100px;
height:100px;
overflow:hidden;
}
.box-1{
background:teal;
float:left;
}
.box-2{
background:blue;
}
<div class="box box-1">box-1</div>
<div class="box box-2">box-2</div>
According to [W3][1],
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.
You edited out the critical bit from the quote. The quote should be
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.
It's the border box of the BFC that must not overlap. The margin box of the BFC is not restricted.
Try this:
.float{
width:200px;
height:300px;
float:left;
background:yellowgreen;
margin-right:10px;
}
.bfc{
overflow:hidden; /*establish a new bfc*/
width:366px;
height:400px;
float: right;
background:blue;
margin-left:50px;/*overlap with div.float*/
}
<div class="float"></div>
<div class="bfc"></div>
I've decreased size of width of .bfc and added float:rigth; to the same selector.
I want a full screen bar in the upper part of my site that has a list in the left and a right part(whatever elements). Why this doesn't work?
#upperline{
background:brown;
width:100%;}
#upperline ul{
float:left;}
#upperline p{
float:right;}
<div id="upperline">
<ul>
<li>our team</li>
<li>help</li>
<li>contact</li>
</ul>
<p>log in</p>
</div>
i am so confused
Put an overflow:hidden; on the parent of floating elements to make it works
See it here
#upperline{
background:brown;
width:100%;
overflow: hidden;}
From this post and adapted to your case :
div is a block-level element (they stretch to 100%
of the parent width).
Now in your example the div contains only floated elements. This makes
it collapse to a height of 0px (It still has 100% width though as you
can see in the example).
Now declaring overflow (any value other than visible) establishes a
new block formatting context, which makes the div contains its
children. Suddenly the div "reappears", not having size 0px anymore.
The reason that your code does't work is that in CSS, putting float: left or float: right makes that element no longer affect its parents height. This means that if you put a float rule on all elements in a container, the container will not have any height. There are a few ways of getting around this.
As Vincent G suggested is putting overflow: hidden on the container div. This works because setting the overflow to hidden (or auto) makes the browser do a different kind of check to see what the height should be. This has a pretty serious downside though. overflow: hidden means that any element that is inside the container that can expand (drop down menu for example) will be cut off.
The second way (and in my opinion the best way) is to place a div with the CSS rule clear:both at the very bottom of the container. This div can be empty so you will not see it. clear: both will put this element below any sibling elements. As long as you don't put a float rule on this element the parent element will be resized to include it.
Here is an example of the second version: https://jsfiddle.net/8ewr89jw/
I've captured an illustration of a CSS two-column layout I've set up, while using the following rule for the orange containers:
.embedded_post{
float: left;
width: 46%;
margin-right: 20px;
padding: 10px;
display: inline-block;
}
As can be seen, the second orange container on the right column is preventing the second orange container on the left column from floating up to the top left box.
This happens apparently since float:left automatically grants the element with a block level flow.
How can I get the second box on the left column to be positioned under the first one?
can you wrap your columns in another pair of divs, so that floating in the right column won't affect floating in the left?
<div id='left_column'>
<div class='embedded_post'></div>
<div class='embedded_post'></div>
</div>
<div id='right_column'>
<div class='embedded_post'></div>
<div class='embedded_post'></div>
<div class='embedded_post'></div>
</div>
css:
#left_column, #right_column {
float:left;
}
you've answered it yourself, there are a couple of options:
trick yourself by granting the div elements with an inline level flow, i.e. specifying display: inline (not recommended).
update the markup to be more semantic and alter the layout to conform to the desired result, e.g. replacing the divs with spans (preferred).
The second div on the left has less width than the rest of the divs, this might have something to do with it. Also, the combination with your (desired) structure and the margin-right isn't how I would do it. In fact, the margin-right may, depending on the with of the parent div of the embedded_post divs, screw up your structure and cause postioning problems.
It works fine when I try it.
p.s. keep in mind that in Firefox, the padding adds to the width/height of the div while this doesn't happen in other browsers.
Suppose I have three <div>s in my page,:
<div id="left" class="test" style="float:left;"></div>
<div id="right" class="test" style="float:right;"></div>
<div id="footer">footer</div>
with this css:
.test{ background:black;height:200px;width:200px;}
#footer{ background:yellow;margin:20px 0 0 0;}
What I want is:
let the "#left" float to left
let the "#right" float to right
change nothing about the "#footer", just set it to margin: 20px;
The result is below:
But I wonder why the floated divs also have the same margin as the #footer. They are floated, so they're independent of the other elements, why would the #footer could affect them?
as well as clear:both on the footer, just adding a container "wrapper" div around the the elements will stop this happening - example
actually adding clear: both; on the footer won't give you a 20px gap between the floats and the footer either, you would actually need to add the 20px bottom margin to the floats - the reasons are all linked.. to clearance or non clearance and it's interaction with Collapsing Margins
Why?
You said you wanted to know why this is happening, in your OP scenario it's because of Collapsing Margins.
You have no clearance involved in the original example, so yes the floats are removed, So the footer margin is still adjoining, therefore collapsing with, the body element, so the body element is the one getting the margin, and then because the floats are still actually inside the body they get the margin too.
As I mentioned above creating a wrapper div to "contain" the floats stops this happening because the rules of collapsing too. However you choose to contain the floats, either with overflow:hidden, or by floating the "wrapper" stops this interaction because .. from the section on collapsing margins:
Vertical margins of elements that
establish new block formatting
contexts (such as floats and elements
with 'overflow' other than 'visible')
do not collapse with their in-flow
children.
you see that both of the properties, float and 'overflow other than visible' are the means to "contain floated children" - actually they're establishing a new block formatting context, but in easy speak most know it as "containing floats" ;)
Now once you have that, that fixes your first bit but then if you decide to introduce clear:both on the footer, the modern browsers will not put a 20px margin between the floats and the footer.. this is actually correct.. from the section on the clear property (my bold):
Then the amount of clearance is set to
the greater of:
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.
The amount necessary to place the top border edge of the block at
its hypothetical position.
In order to place the top edge of the footer below the floats (in your example) the browser has to introduce 200px of clearance, which is far more than 20px so it follows rule 1. If your top margin on the footer was 220px, the margin would be greater than the any clearance needed, so it would follow rule 2.
So, if you did actually want the footer to be 20px below the floats no matter what their heights are, you would put the 20px as a bottom margin onto the two floats, so it [the footer] would clear, via clearance rule 1, the floats with the required gap/margin, no matter which was float the longest.
PS: Don't test the above in IE7 or below - and I hope it wasn't too boring ;)
Add a clear: both to the #footer CSS. That should make the footer render below the floating divs with the margin you want.
Try this and this may solve your problem:
<div id="right" class="test" style="float:right;"></div>
<div id="left" class="test"></div>
<div id="footer">footer</div>
CSS remaining unchanged.
I made a test before finding this page that has two boxes, with the right-floated one being affected by the left block one (which comes after it) here: http://jsfiddle.net/4r75s/
The overflow trick that prevents parent divs collapsing when they only contain floated content seems to work here, that is setting overflow to hidden, auto or scroll. I wrapped them in a containing div to do it and it works: http://jsfiddle.net/4r75s/1/
#container {
overflow: hidden;
}