I'm currently trying to create a two column layout where the left column is floated and the right column restrains the float by forming a new block formatting context. That works. Later, I try to put some visible space between the left column and the content of right column. If I set left padding on the right column, it works. I also try to replace the left padding with left margin on the right column, and think they would have the same effect. However, to my surprise, the left margin is not working at all
I reproduce the problem with the following code. Notice that the example in the middle, setting the left margin on the right column does not really push it away from the left column
* {
margin: 0;
padding: 0;
}
.container {
height: 50px;
line-height: 50px;
width: 500px;
}
.container + .container {
margin-top: 20px
}
.left {
float: left;
width: 150px;
height: 100%;
background: orange;
text-align: center;
}
.right {
overflow: auto;
background: skyblue;
height: 100%;
}
.with-padding {
padding-left: 30px;
}
.with-margin {
margin-left: 30px;
}
<div class="container">
<div class="left">left column, floated</div>
<div class="right with-padding">
<p>left padding works</p>
</div>
</div>
<div class="container">
<div class="left">left column, floated</div>
<div class="right with-margin">
<p>left margin dost not work</p>
</div>
</div>
<div class="container">
<div class="left">left column, floated</div>
<div class="right">
<div class="with-margin">
<p>left margin works on the wrapper div</p>
</div>
</div>
</div>
I did search for this topic on the internet but don't find too much relevant information. I suspect this might be related to the concept of block formatting context (BFC). If I understand correctly, margin represents distance between the target box's outer edge and containing BFC's inner border.
If we set margin on a box which itself forms its own BFC, then margin shouldn't work? So in the third example, I place the text into an extra wrapper and set margin on that wrapper, and it looks like left margin work again. However, this is just my guess.
The critical point here is that
The border box of ... 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 ...
CSS 2.2 Section 9.5 Floats
So the margin, which lies outside the border box, of such a BFC can (and in your second case does) overlap with the float, but padding, which lies inside the border box, cannot.
Related
There are multiple ways posted here to uncollapse a vertical PARENT margin, but nothing about uncollapsing vertical margins of adjacent elements. The only solution I found was in this answer (back in 2009):
<div style="overflow: hidden; height: 0px; width: 0px;"> </div>
Almost 7 years passed since there. Is some better way to do this (possibly using some CSS3)?
Basically, suppose you have: http://jsfiddle.net/ok2u3o3c/
<div class="one"></div>
<div class="two"></div>
div {
width: 300px;
height: 200px;
}
.one {
margin-bottom: 10px;
background-color: blue;
}
.two {
margin-top: 20px;
background-color: red;
}
What would be the most elegant way to make the distance between these 2 boxes 30px instead of 20px (where the first margin contributes 10px and doesn't collapses)?
Let's start with the relevant documentation explaining the behavior of collapsing margins:
8 Box model - 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.
The following rules apply, which means that there are a things that you can do to prevent the margins from collapsing for sibling elements:
Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children)
Therefore if you float the elements with collapsing margins, they will no longer collapse:
.collapsing-margins {
margin: 100px 0;
background: #f00;
float: left;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>
Margins of inline-block boxes do not collapse (not even with their in-flow children).
Therefore you could also add change the display of the elements to inline-block:
.collapsing-margins {
margin: 100px 0;
background: #f00;
display: inline-block;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>
I have a container with 3 children elements.
<div class="container">
<img />
<div class="element1"></div>
<div class="element2 bottom"></div>
</div>
They must be positioned as shown on the diagram below:
image is in the top of the left column and nothing goes below it (it is the only element in the left column)
element1 is in the top of the right column
element2 is stick to the bottom of the right column (and must not collide with the element1 which is above it)
Does somebody know how to achieve such layout using pure CSS? Ideally I wouldn't like to add any markup, but I can do that if that's the only possible way.
The biggest problem I'm facing here is how to stick that second element (non-image) to the bottom of the container without removing it from the flow. Because if I use position: absolute and remove it from the flow, the elment above it can collide with it (both elements have unknown height).
Here's a pen to work on: http://codepen.io/anon/pen/yNwGvQ
I would suggest you to use two columns in your html and then use the property display: flex; for your right column as suggested in the article A Complete Guide to Flexbox.
http://codepen.io/AlexisBertin/pen/QboYyY
All the HTML:
<div class="container">
<div class="column column-left">
<div class="image">This is an image</div>
</div>
<div class="column column-right">
<div class="element1">This container has dynamic content so it's height is unknown and may change.<br/><br/> Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</div>
<div class="element2">This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
Part of this CSS:
.column {
float: left;
height: 100%;
}
.column.column-left { width: 100px; }
.column.column-right {
width: calc(100% - 100px);
display: flex;
flex-direction: column;
justify-content: space-between;
}
Hope you get the idea. Good Luck'.
EDIT:
The easiest way to achieve this without declaring height to the container seems to only create a third parent div to the first block of the second column and define it as flex: 1; while the second block of this same second column would be define as flex: 0;.
http://codepen.io/anon/pen/yNwZmJ
More details explained in the comments.
The easiest solution I figured out is this one:
First you create this CSS:
.container {
width: 400px;
padding: 10px;
border: 1px solid red;
background-color: white;
}
.container > img {
float: left;
}
.container > div {
position: relative;
overflow: auto;
padding-left: 5px;
min-height: 120px;
}
.container > div > .bottom{
position: absolute;
bottom: 0;
display: block;
}
And then use these divs, depending on your content. The first one you use when you know your text is short:
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
</div>
<div class="bottom">
<p>This container also has dynamic content so it's height is unknown and may change</div>
</div>
</div>
The second one you use when you know your text is long
<div class="container">
<img src="http://placehold.it/120x120">
<div>
<div>
<p>This container has dynamic content so it's height is unknown and may change.</p>
<p>Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger. Some random content to make it larger.</p>
</div>
<div>
<p>This container also has dynamic content so it's height is unknown and may change</p>
</div>
</div>
</div>
The difference is that you remove bottom class from the last div in your div that has long text.
Also in your CSS you can see .container > div{... min-height: 120px; ...}, you should set it to height of your image. In case you want the bottom text more down then you have to increase min-height to be bigger than your image height.
Here is it in action: http://codepen.io/anon/pen/YXgBXx
I have something like this:
<div class="top">top</div>
<div class="container">
<div class="left">left</div>
<div class="right">right</div>
</div>
<div class="bottom">bottom</div>
Relevant code in jsFiddle
As you can see, between top and bottom divs, there is a div container. I want this div container to move bottom dive as much as is needed (and i don't want it to be a fixed value - that means if, lets say left container will get much higher - the bottom div will be pushed down as well.
How can i do that?
This is a simple seeming problem that ends up being kind of tricky. The above suggestion about position:relative vs. position:absolute are a good first step. After that you need to make some room for the set width right div:
.left {
height: 100%;
min-height: 50px;
border:1px dashed red;
padding-right: 50px; <---
}
Then float your right div in the space you made:
.right {
float:right; <---
width: 50px; (This needs to match the padding-right value above.)
text-align: right;
min-height: 50px;
height: 100%;
border:1px dashed blue;
}
Finally, put the right div before the left div in the html:
<div class="top">top</div>
<div class="container">
<div class="right">right</div>
<div class="left">left</div>
</div>
<div class="bottom">bottom</div>
(Tested in Chrome and IE.)
See: Right div fix width, left div extend to max width?
You can check out a fiddle here: http://jsfiddle.net/x3QfG/1/
Will that work for you?
Right now you're using absolute positions for the left/right div's, so you will always need to know the height in order to position the bottom div correctly. What you want to do is float these instead, then clear the floats in the bottom div. That way the left/right can be as high as their contents, and the bottom div will always appear below.
.bottom {
clear: both;
}
.left {
float: left;
width: 50%;
min-height: 50px;
}
.right {
float: right;
width: 50%;
min-height: 150px;
}
I've modified your jsFiddle accordingly, and made the right div higher to show how the bottom always appears below.
Use floats rather than positioning them absolutely. That will make your architecture very much fluid and flexible.
After you apply necessary float values to your .left and .right, use a clearfix hack to contain your floated elements within the container. Now whenever any of the .left or .right divs increase in height, the bottom div will be pushed down.
Make Container Relative and left and right absolute,and for positioning set width rather than using float.
How does the CSS Block Formatting Context work?
CSS2.1 specifications says that in a block formatting context, boxes are laid out vertically, starting at the top. This happens even if there are floated elements in the way, except if the block box established a new block formatting context. As we know, when browsers render block boxes in a block formatting context, the floated element is omitted, why does establishing a new block formatting context work?
How are boxes (block boxes and inline boxes) laid out in the normal flow?
I read somewhere that block elements generate block boxes, but floating elements are ignored when a user agent draws box and take them into account when they fill out content. Whilst floating elements will overlap other elements's boundary of the boxes, the solution is establishing a new block formatting context for the overlapped elements using overflow:hidden.
"New block formatting context is still block formatting", so when drawing a box, it will also treat the floating element as if it doesn't exit. Is that right or have I misunderstood "new block formatting context?"
Update:more questions
By saying "It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code."
I don't understand the meaning, I will provide an example, maybe you will understand me.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<h3>This is a content box</h3>
<p>It contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float. This is normal behaviour.</p>
<div class="float">floated box</div>
</div>
I thought the floating box should float to the top of the containg block-the div with class content. Besides, if the floating box appears earlier in the markup, then it will display what I think it should be.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
How can we explain this? Can we use "block formatting context and inline formatting context" to explain it?
Block Formatting Contexts
Floats, absolutely positioned
elements, block containers (such as
inline-blocks, table-cells, and
table-captions) that are not block
boxes, and block boxes with 'overflow'
other than 'visible' (except when that
value has been propagated to the
viewport) establish new block formatting contexts for their contents.
With my bold, it's the establish bit that is important
What this means is that the element you use overflow (anything other than visible) or float or inline-block..etc on becomes responsible for the layout of its child elements. It's the child elements which are then "contained", whether that's floats or collapsing margins they should be wholly contained by their bounding parent.
In a block formatting context, each
box's left outer edge touches the left
edge of the containing block (for
right-to-left formatting, right edges
touch)
What the above line means:
Because a box can only be rectangular and not irregular shaped this means a new block formatting context between two floats (or even beside one) will not wrap around neighbouring side floats. The inner, child boxes can only extend as far as to touch their parents left (or right in RTL) edge. It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code.
Float Clearance
In normal circumstances floats are supposed to clear all previous floated elements, that's previously floated in the whole source code, not just your displayed "column"
The telling quote from the "float clearance specs" is:
This property indicates which sides of
an element's box(es) may not be
adjacent to an earlier floating box.
The 'clear' property does not consider
floats inside the element itself or in other block formatting contexts
So say you have a three column layout where the left and right columns are floated left and right respectively, the side columns are now in new Block Formatting Contexts, because they are floated (remember float is also one of the properties that establish a new BFC), so you can happily float list elements inside them and they only clear floats which are already inside the side columns they no longer care about floats previously in the source code
##To make the main content a new Block Formatting Context or not?
Now that middle column, you can simply margin it from both sides so that it appears to sit neatly between the two side floated columns and take the remaining width, a common way to get desired width if the centre column is "fluid" - which will be fine until you need to use floats/clearance inside your content div (a common occurrence for those using "clearfix" hacks or templates including them)
Take this very simple code:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated">this a floated box</div>
</div>
It produces the following:
In general this is fine, especially if you have no background colours or internal (in the main content) floats - notice the floats are fine (not cleared yet) they're doing probably what you except them to but they, the H3's top margin and the p's bottom margin are not actually really contained by the content div (lightgrey background).
So to the same simple margined scenario of above code add:
.clear-r {clear: right;}
to the CSS, and change the second HTML floated box to:
<div class="floated clear-r"> this a floated cleared box</div>
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This time you get this:
The second float is clearing the right side but it's clearing the whole height of the right column. The right column is floated earlier in the source code so it's clearing it as told! Probably not the desired effect though, also note the h3 and p margins are still collapsed (not contained).
###Make it establish a Block Formatting Context, for the sake of the children!
and finally make the main content column take responsibility - become a Block Formatting Context - for its contents : remove margin: 0 200px; from the main content CSS and ADD overflow: hidden; and you get this:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
overflow: hidden;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This is probably much more like what you would expect to happen, note now the floats are contained, they clear properly ignoring the right side column, and also the h3 and p margins are contained instead of collapsed.
With the extensive use of resets these days the margins are less noticeable (and IE still doesn't get them quite right) however what just happened to the centre "main content" is that it became a Block Formatting Context and is now responsible for its own child (descendant) elements. It's actually very similar to Microsoft's early days notion of hasLayout, it uses the same properties display: inline-block, float, and overflow anything other than visible, and of course table cells always have layout.. it is however without the bugs ;)
##Update: re more information in question:
When you say "but floating elements are ignored when user agent draws box and take them into account when they fill out content."
Yes floats normally overlay their container boxes, is that what you mean about parent boundaries? When a block element is drawn and it contains a float the block parent itself is drawn as a rectangle under the float and it is the "inline anonymous boxes" or simply "line boxes" of the other child elements that are shortened to make room for the float
Take this code:
#content {
background: #eee;
color #000;
border: 3px solid #444;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: left;
margin: 10px;
}
p {
background: #444;
color: #fff;
}
<div id="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
Which produces this:
You see that the parent element doesn't actually contain the float, as in it doesn't wrap it entirely.. the float is simply floating on top of the content - if you were to keep adding content to the div it would eventually wrap underneath the float because there would be no need for the (anonymous) "line boxes" of the p element to shorten themselves any more.
I've coloured the paragraph element so you can see that it too actually goes under the float, the darkgray background is where the paragraph starts, the white text is where the "anonymous line box" starts - it's only actually them that "make room" for the float, unless you tell it otherwise (i.e. you change the context)
Again referring to the above picture, if you were to margin the left side of thep element, yes it will stop the text wrapping under the bottom of the float because the "line boxes" (the white text) will only touch the left edge of their container, and you will bring the coloured background of the p to the right, clear of the float, but you won't have changed the behaviour of the p's formatting context. Like the centre column in the first picture way above ;)
I have a div that holds some text, it has a background with a border, but for some reason the box is not expanding to the text, even with overflow: auto; here is my script for the box as well as a picture:
.box { background: #ffdcba; border: 1px solid #f78d25; display: block; clear: both; margin: 4px 0px; padding-left: 15px; overflow: auto; }
the divs inside are just floating, left and right, and have display: inline on them. heres a picture:
http://i45.tinypic.com/2woj1br.gif
A floated box will not expand to fit its contents. You need to add a clearing element after your content. <br> is usually good.
YOu don't specify the exact construction of the HTML, but I"m asssuming you've got something like this:
<div class="box">
<div style="float: left">test subject></div>
<div style="float: right">
<div>ASD</div>
etc...
</div>
</div>
Floating elements removes them from the regular flow and will cause the "overflow" you are seeing. You need to add a non-floated element below the floated parts to force the containing div.box to "expand" to contain the floats:
<div class="box">
<div style="blah blah" ....
etc....
<br style="clear: both" />
</div>
As well, the overflow: auto will not have any effect on your .box style, because it does not specify any height or width - it will naturally just expand to contain whatever content you put in there. To force a scrollbar to appear, you need to put in either height or width styling, and enough content to exceed either of the limits.