What's the impact of the negative bottom margin - css

First, here is the html:
<div class="first">
<div class="second">
<div class="third">
Hello, margin collapsing!
</div>
</div>
</div>
Then here is the CSS:
.first {
background-color: red;
padding: 20px;
}
.second {
background-color: green;
margin-bottom: -20px;
}
.third {
background-color: yellow;
margin-bottom: 20px;
}
In the final layout, the third div looks like it doesn't have the bottom margin. I know it must be the effect of the second div whose bottom margin is negative. But I don't understand how it works. Could you please provide an explanation?

Padding - Creates, easy said, a invisible border inside your element. You provide with it the spaces inside of your element (arround the content).
.first {
background-color: red;
padding: 20px;
}
So here you tell, any content of first hast to be 20px away from each side (each side cause you did not provide any declaration like padding-top)
Margin - On the other hand creates the opposite, it creates space arround your element.
.second {
background-color: green;
margin-bottom: -20px;
}
So this one says the second block has a space on the bottom outside. Its defined negative, which means the following items float in your element.
This explains it awfully: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model

Related

What does collapsing width height and margin mean for block level elements?

What does it mean that the width of block level elements can not be collapsed but the height can?
And can you please explain this text from the W3.org specification:
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 meaning of the word collapse is causing much of the confusion here.
A collapsed margin is the name given to the instance when margins of two different elements occupy the same space.
Consider the following example:
.box {
height: 50px;
width: 50px;
}
.box1 {
background: red;
margin-bottom: 25px;
}
.box2 {
background: blue;
margin-top: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
It's difficult to tell, but that whitespace between the two boxes is only 50px. You might think it should be 75px, because I've specified a margin-bottom of 25px on the top box, and a margin-top of 50px on the bottom box. 25 + 50 = 75, so why is the whitespace only 50px?
Well, margins can't have any content within them; a margin is specifically denoting a lack of content. Considering there is no content to display in a margin, the parser thinks they might as well be combined to optimise space.
The word 'collapsed' comes about because there are technically two different 'segments' of margins existing in the same place at the same time, 'collapsing' in on each other.
Note that this doesn't happen with margin-left and margin-right:
.box {
height: 50px;
width: 50px;
float: left;
}
.box1 {
background: red;
margin-right: 25px;
}
.box2 {
background: blue;
margin-left: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
The space above is indeed 75px. This can be a confusing concept to wrap your head around, but it's important to note that it only affects vertical margins. Further information about collapsing margins can be found at CSS Tricks and Mozilla.
It's also important to note that, by default, a block-level element takes up 100% of the width of its parent, but 0% of the height.
Here's an example illustrating this:
.parent {
background: blue;
border: 10px solid purple;
height: 50px;
width: 200px;
}
.child {
background: red;
}
<div class="parent">
<div class="child">Text</div>
</div>
In the above example, I specify both a width and a height on the parent, though I don't specify either on the child. As you can see, the child element inherits the 200px width, but does not inherit the 50px height.
Hopefully this helps clarify that a bit!

struggling to understand how "collapsed margin" works for "parent and first/last child"

I'm currently learning about collapsed margin on MDN. Collapsed margin for adjacent siblings are pretty easy to understand. However, I'm struggling to understand how collapsed margin works for "parent and first/last child", especially the last sentence highlighted in blue.
How could a collapsed margins ends up outside the parent? I can't seem to picture it in my head.
Does this help? The 50px margin on .outer and 100px margin on .inner have collapsed to a single 100px margin from .inner, and .inner's margin is outside of the parent, rather than inside like you would expect.
.outer {
background: #eee;
height: 100px;
}
.inner {
background: red;
height: 10px;
margin-top: 100px;
}
<div class="outer">
<div class="inner"></div>
</div>
Here is an interesting example for you. The first parent does not have a border set. It automatically assumes that you do not want to use the child margin, yet it does stand of from the top.
The second example does have a border set. Now the browser assumes you explicitly want to make use of the native margin of the h1 tag and the parent adapts to show this.
.parent1 {
background-color: #669;
}
.parent2 {
background-color: #969;
border: 1px solid #000;
}
<div class="parent1">
<h1>hello World</h1>
</div>
<div class="parent2">
<h1>hello World</h1>
</div>

Why does this floating parent calculate it's width before taking sibling into account?

I am trying to understand why the .item-wrap in the css below only calculates it's width *as if .floatleft2 wasn't there, and yet the .items contained by .item-wrap clearly are aware that .floatleft2 is there.
I want the .containingbox to "shrink wrap" the content, but not for the .items to wrap "prematurely" i.e. while there is still extra screen space. (see 'working' fix below).
I have already found the workaround, but what specification in CSS causes this interaction between .floatleft2, .item-wrap, and .item such that the .item-wrap width isn't wide enough to incorporate all the .items?
jsfiddle demo (code reproduced below)
jsfiddle demo with 'working' inline fix
<body>
<h1>float:left on .containingbox, with item-wrap, with floatleft2, causes premature wrapping of .item</h1>
<div class="containingbox">
<div class="floatleft2"></div>
<div class="item-wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
</body>
body {
margin: 20px;
padding: 0;
font: normal 85% arial, helvetica, sans-serif;
color: #000;
background-color: #fff;
}
.containingbox {
height: 200px;
border: 1px solid #000;
float: left;
}
.item-wrap {
border: 1px solid #0FC;
height: 3px;
}
.item {
border: 1px solid #F09;
width: 50px;
float: left;
position: relative;
margin: 0 10px 0 0;
height: 75px;
}
.item::before { content: "item"; position: absolute; }
.floatleft2 {
height: 75px;
background-color: #000;
border: 1px solid #000;
float: left;
margin: 0 10px;
display: block;
width: 50px;
}
When you float .floatleft2 but not .item-wrap, .floatleft2 is taken out of the normal flow of the container box (which gets its own block formatting context from being floated itself), and .item-wrap is laid out as though .floatleft2 were not there. Since .item-wrap is not floated, it behaves like a regular block-level element, using the auto width and stretching to fit the container as per section 10.3.3.
The reason why the container is sized horizontally to just fit .item-wrap and its floated items is because, when .floatleft2 is taken out of the normal flow of the container, the container no longer needs to account for the size of .floatleft2. It only accounts for the contents of .item-wrap, which are themselves also floated.
The width of a floating element, when no explicit width is specified (it uses the auto width), is shrink-to-fit, according to section 10.3.5. CSS2.1 does not say how to implement shrink-to-fit, but it does say that an implementation should use shrink-to-fit. In this case, the container is shrunk to just the minimum width needed to fit the floating items on one line. The width of .item-wrap is never relevant except that it should stretch to fit within the bounds established by the container, as mentioned above.
What happens then is that when .floatleft2 is introduced, the floating items float to the left of that element (the same fundamental behavior you see when floating the items themselves), regardless of the layout of .item-wrap or the container. This causes some of the items to wrap to the next line since neither container element changes its size to account for .floatleft2.
you are making the div class from block level element to inline element.
you should use float:left; and remove the display:inline
.item-wrap {
/*display: inline;*/
float: left;
border: 1px solid #0FC;
height: 3px;
}
Here is the Working Demo. http://jsbin.com/vicusesu/1/edit
It is because you have float: left on .float-left2 and .item but not on .item-wrap. This effectively removes all floated items from item-wrap (they are floated) while it still keeps the same width as if they were there.
If you add a float (left or right) to your .item-wrap you will not have this issue.

Is there any difference between float: none and clear: none

I was doing a bit of stuff using float and clear. I found no difference using float: none; or clear: none; Is there any? can anybody illustrate the difference with an example
Float:none; tells the elements that you do not wish for it to float.
Clear tells other elements whether they should be allowed to float or not, and in the case of none, you're allowing floats on both sides. it's why when you use clear:both; that floating stops.
They're two totally different things.
float will make an element align to the left or right (the parameter) inside its parent. float: none does nothing, unless the element was already floating. The float element lose it's automatically filled width, and reduce it to as small as it can get.
clear will make sure there are no floating elements on the side you tell. If there is one, it will move down until there is none in the given direction. clear: both will check this for both directions.
Here's an illustration to show you what floats and clears do.
It seems like you didn't understand the underlying concept of what float does. Any value of float except none whenever assigned to a block-level element takes that element out of the document flow. Suppose you have two different div elements, one with float:none and the other with clear:none. Now the later could be either in the document flow or out of the flow of document -- depending upon its float value. I present you two examples. In the first version the Red paragraph uses float:none and in the second version the Red paragraph uses clear: none
Red paragraph using float:none:
#usefloatnone
{
border: 1px dotted black;
background-color: red;
width: 1050px; height: 350px;
float: none;
}
#useclearnone
{
border: 1px dotted black;
background-color: red;
width: 1050px; height: 200px;
float: right;
clear: none;
}
#normal
{
border: 1px dotted black;
width: 1050px; height: 100px;
}
</style>
</head>
<p id="usefloatnone"> Red paragraph </p>
<p id="normal"> Normal paragraph </p>
<p id="normal"> Normal paragraph </p>
<p id="normal"> Normal paragraph </p>
</html>
Red paragraph using clear:none:
#usefloatnone
{
border: 1px dotted black;
background-color: red;
width: 1050px; height: 350px;
float: none;
}
#useclearnone
{
border: 1px dotted black;
background-color: red;
width: 1050px; height: 200px;
float: right;
clear: none;
}
#normal
{
border: 1px dotted black;
width: 1050px; height: 100px;
}
<p id="useclearnone"> Red paragraph </p>
<p id="normal"> Normal paragraph </p>
<p id="normal"> Normal paragraph </p>
<p id="normal"> Normal paragraph </p>
You can see the difference in effect while using clear: none and float: none now. I suggest you to first thoroughly understand the concept of float and clear from this tutorial by w3.org community. You use clear property upon elements when you want to clear any floating elements around/(usually left or right to) them.
Float none stops an element to stop wraping around adjacent floating child Elements. By default all, elements have float none.
Clear both stop element to wrap around any floating child from left or right side.
For more details and live examples, visit my tutorial,
http://tutorial.techaltum.com/css_float.html.

balanced alternating column layout in CSS3

I'm trying create a balanced (2-) column-layout.
The content is not text but blocks and varies in height.
The content should be placed alternatingly left and right, as long as "left" and "right" have (roughly) the same height..
I.e. in this image:
The space between 1 and 3's shouldn't be there.
Or in this image:
the 2's should stand alone on the right side and the 1, 3's and 4 should stand on the left side (without space between them).
I tried using "floating <li>'s" like this:
HTML:
<ol class="context">
<li class="gruppe">1</li>
<li class="gruppe">2.0<br />2.1</li>
<li class="gruppe">3.0<br />3.1</li>
<li class="gruppe">4</li>
</ol>
CSS:
ol.context
{
border: 1px solid #048;
list-style: none;
margin: 0;
padding: 0 0 8px 0;
overflow: auto;
}
li.gruppe
{
background: #048;
color: white;
float: left;
font: bold 32px Arial, sans-serif;
margin: 1px;
text-align: center;
width: calc(50% - 2px);
}
(See attempt 1 and attempt 2)
I have also tried to use column's (column-count: 2; column-fill: auto;) but this does not fill the columns left-to-right first. (It fills top-to-bottom first.)
Is this even possible without JavaScript?
I would say this is not possible without JS. Here is a fiddle I made based on an article from Ben Holland. At least to me looks like what you are after.
http://jsfiddle.net/QWsBJ/2/
HTML:
<body onload="setupBlocks();">
<div class="block">
<p>***Content***</p>
</div>
<div class="block">
<p>***Content***</p>
</div>
<div class="block">
<p>***Content***</p>
</div>
<div class="block">
<p>***Content***</p>
</div>
<div class="block">
<p>***Content***</p>
</div>
</body>
CSS:
.block {
position: absolute;
background: #eee;
padding: 20px;
width: 300px;
border: 1px solid #ddd;
}
JS:
var colCount = 0;
var colWidth = 0;
var margin = 20;
var blocks = [];
$(function(){
$(window).resize(setupBlocks);
});
function setupBlocks() {
colWidth = $('.block').outerWidth();
colCount = 2
for(var i=0;i<colCount;i++){
blocks.push(margin);
}
positionBlocks();
}
function positionBlocks() {
$('.block').each(function(){
var min = Array.min(blocks);
var index = $.inArray(min, blocks);
var leftPos = margin+(index*(colWidth+margin));
$(this).css({
'left':leftPos+'px',
'top':min+'px'
});
blocks[index] = min+$(this).outerHeight()+margin;
});
}
Array.min = function(array) {
return Math.min.apply(Math, array);
};
Updated: I believe this is almost impossible to achieve with CSS only. There are many different solutions, but they all require some compromises unless you are willing to use JavaScript or some server-side code.
Using CSS columns
Here's an alternate fiddle using reordered blocks. Here's a fiddle demo using CSS columns without reordering.
You can use CSS colunms to change your block flow to vertical unless you alter the order of their output. If you can output odd numbers first, then even numbers, you win.
<div class="wrapper">
<div class="block1">1</div>
<div class="block3">3</div>
<div class="block2">2</div>
<div class="block6">4</div>
</div>
.wrapper {
column-count: 2;
column-width: 100px;
-moz-column-width: 100px;
-webkit-column-width: 100px;
width: 260px;
}
div {
border: 1px solid #999;
display: inline-block;
margin: 10px;
width: 100px;
}
.block1 { height: 100px; }
.block2 { height: 130px; }
.block3 { height: 150px; }
.block4 { height: 100px; }
This solution is not compatible with IE9 and below.
Block Height Known
If you do know your block heights you can solve this problem by using absolute positioning.
block1 {
height: 100px;
position: absolute;
left: 0;
top: 0;
}
block2 {
height: 110px;
position: absolute;
left: 0;
top: 100px; /* The height of the div above it */
}
A big drawback is dynamic content; we seldom know block height. So this solution is very limited in its application unless you are willing to calculate the height block height.
If you are willing to use JS
Use a plugin like Masonry. Both in vanilla js or jQuery flavour.
Other Options
This leaves you with the following options that require some compromises.
Group your blocks into columns. See this Fiddle for a demo. This will alter the flow of your blocks to vertical, then horizontal.
Use display: inline-block; vertical-align: top; on your blocks. This will leave some white space below your blocks.
Force the height of your blocks, rendering this a non-issue. For blocks with additional content use the overflow property to allow in-block scrolling.
As others have commented, you could attempt to calculate the height of the blocks on the server.
You could try a mix of flex and float (only tested in Firefox/IE10 and safari 5.1.7 , cause to my own opinion, CSS is not your solution)
http://codepen.io/gcyrillus/pen/zgAiw
But, in any CSS case you choose, the best is to relay on the mansonry script.
CSS is not really adapted to this kind of layout. At this time you have many CSS method for layout and basicly: display and float.
You can easily use this together within your html tree structure but this methods are not meant to be mixed. A boxe will be floatting, an inline-level-box or block-level-box and each are suppose to interact in the flow.
Float, breaks a line before itself after a non floatting element or slides down untill it has enough room, that you dispatch right/left via CSS r not.
inline-block moves away from floatting elements and breaks a line if not enough room left, floatting elements among inline-blocks will keep breaking a line before floating.
Column CSS will fill columns with content one by one. see : http://codepen.io/gcyrillus/pen/AtazJ
Inline-flex elements seems to work with floatting elements ... but is it suppose to untill it's a validated rule ?
What seems to be wised to me , is to used a javascript for the layout expected and relay on float or display:inline-block + width as a fall back.
Last solution is to think this ahead on your server side and dispatch your items in 2 containers with another appropriate markup if that is possible ( no idea of your real life content dispatched in your ol li ).
The CSS for the FLEX test :
li.gruppe
{
background: #048;
color: white;
font: bold 32px Arial, sans-serif;
text-align: center;
box-sizing:border-box;
border-bottom:1px solid white;
border-bottom:1px solid white;
display: -webkit-inline-flex;
display: -moz-inline-flex;
display: -ms-inline-flex;
display: inline-flex;
width:50%;
}
li:nth-child(even){
float:right;
clear:right;
border-left:1px solid white;
margin-top:0;
}
EDIT: This is an interesting solution, but unfortunately it does not solve the problem that was asked for.
The solution I propose here puts subsequent elements into alternating columns, so: 1 -> left, 2 -> right, 3 -> left, 4 -> right, etc.
This is a interesting problem by itself, but not what was asked for.
Thanks to #Nils in the comments for pointing this out.
Original answer
Here is my attempt with flex!
https://jsfiddle.net/vqLr8t3e/
I am not sure if it works in IE11.
Code
.the-beginning {
background: green;
color: white;
font-weight: bold;
text-align: center;
cursor: pointer;
}
.the-end {
background: red;
color: white;
font-weight: bold;
text-align: center;
cursor: pointer;
}
.container-outer {
overflow: hidden;
}
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
max-height: 19999px;
margin-top: -10000px;
}
.container > div {
width: 50%;
box-sizing: border-box;
border: 5px solid grey;
padding: 5px;
background: white;
order: 1;
}
.container > div:nth-child(odd) {
order: -1;
}
.container > div:nth-child(1),
.container > div:nth-child(2) {
margin-top: 10000px;
}
<div class="the-beginning">THE BEGINNING</div>
<div class="container-outer">
<div class="container">
<div>LEFT 0</div>
<div>RIGHT 0<br>RIGHT 0</div>
<div>LEFT 1<br>LEFT 1<br>LEFT 1</div>
<div>RIGHT 1</div>
<div>LEFT 2</div>
<div>RIGHT 2<br>RIGHT 2<br>RIGHT 2</div>
</div>
</div>
<div class="the-end">THE END</div>
Idea
Use flex-direction: column; and flex-wrap: wrap; on the container, and width: 50%; on the items, as a first step towards showing the items in columns.
Use order: -1; and order: 1 to sort odd and even elements into different columns.
Use a gratuitous margin-top: 10000px; on the first element of each column, and a max-height: 19999px; on the container, so that no two such items fit into one column. This will make sure each of these items starts in a new column. Compensate with a negative margin-top on the container. Cut it off with an outer container with overflow: hidden;.
I'm not sure if I got this right . .
"the 2's should stand alone on the right side and the 1, 3's and 4
should stand on the left side (without space between them)."
html:
<div id="box">
<div class="data">1</div>
<div class="data" style="float:right">2<br/>2<br/>2<br/>2</div>
<div class="data">3<br/>3</div>
<div class="data">4</div>
</div>
css:
#box {
width:100%;
height:auto;
float:left;
}
.data {
height:auto;
width:50%;
float:left;
background-color:#ccc;
border-bottom:solid 1px #000;
}
Fid:
http://jsfiddle.net/YdEW9/26/
This is pure css. Everything is floated left then gave inline-css to float:right on the div with (4) 2's
I kinda don't know how to set the inline-css without javascript. Server side maybe? but I doubt you can get the height of the elements.
Well anyway hope this helps.
PURE CSS SOLUTION:
Add the following to your css file:
ol.context li:nth-child(even) {
float: right;
}
DO NOT change your html or anything else.
Result in FF:
--
How it Works
Instead of floating all your elements "left" and creating gaps, we float each container according to the side/column the element they reside in.

Resources