It is said that
The floated box is shifted to the left or right until its margin edge touches the padding edge of the containing block, or the margin edge of another floated element
However i do not think this is correct.
Could you please provide an example or an explanation of the correct interpretation (both cases ; for a first child div and other sibling divs)
See this demo page.
The floated box is shifted to the left or right until its margin edge touches the padding edge of the containing block
Correct. The div labeled First item is offset a total of 30px, due to the 10px of padding on its container and 20px due to its margins.
or the margin edge of another floated element
Also correct. Notice how there is a total of 40px between First item and Second item, due to both elements having 20px of margins all around.
Updated for comments: Floating an element takes it out of the normal flow of the document. That is, non-floated elements will not "make room" for floated elements. In most examples, using overflow: hidden; to clear floats is equivalent to the other methods, so I use it because it's less markup. For more info, see the Quirksmode article and a counter example for overflow hidden.
here is a simple example..
<style>
.size1{
width: 50px;
height: 50px;
margin: 10px;
}
.size2{
width: 400px;
height: 400px;
padding:50px;
}
.red{background-color:red;}
.blue{background-color:blue;}
.yellow{background-color:yellow;}
</style>
<div class='size2 red'>
<div class='size1 blue' style='float:right'></div>
<div class='size1 yellow' style='float:right'></div>
</div>
Your quote is correct
The floated box is shifted to the left or right until its margin edge
touches the padding edge of the containing block, or the margin edge
of another floated element
Basically it mirrors the spec:
“Floated boxes will move to the left or right until their outer edge
touches the containing block edge or the outer edge of another float.”
http://www.w3.org/TR/CSS2/visuren.html#floats
As far as I know, outer edge also includes margin and padding.
See this example here, where margin and padding are respected.
Example: http://jsfiddle.net/jasongennaro/K6mK5/
Related
I know that float takes element out of normal flow partially. What came to my surprise was that I observed the next div of a floating div taking the space of floating div too. Example:
.header {
float: left;
height: 100px;
width: 100%;
background: green;
padding: 10px;
background-clip: content-box;
}
.footer {
background: yellow;
border: 1px dotted red;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="header">HEADER</div>
<div class="footer">FOOTER is yellow with red border</div>
</div>
Here the yellow footer comes after the green header, but the way it behaves is as if it were the container of header. The border is drawn out of both header and footer. In principle, footer should draw border only around itself. What is the reason for this behavior?
Another surprise is the green color(background) opaques the yellow one. Is this how stacking order works? Is it normal that div later in html order comes below the former divs in stacking order?
Please give reference to explain the behaviors.
This behaviour occurs because as you said floated elements are taken out of flow and so the element next to them rise up and take their space. But floated elements are taken out of flow only partially. Partially, in the sense that elements next to them ignore them like they are not in flow anymore, but the content within them still respects these floated elements and hence is pushed horizontally or vertically.
You can think of floated elements like floating above other elements. Also, the content of other elements is also sort of risen above them. So, their plane (or background-color) goes below floated elements, but the content is pushed downwards (or horizontally).
Regarding your particular case, you should remember that element next to floated element always takes up the space of the floated element. In your demo, it seems slightly confusing because your floated element has width set to 100% which causes the next element's content to push downwards not only horizontally. Hope that makes sense?
Analogy
As an analogy, you can think of all html elements as 1cm thick wooden boards, with some thick alphabet blocks placed above them. Somewhat like this image below:
Now suppose you have two such elements, first is header with alphabets HEADER and below that footer with alphabets FOOTER.
When you apply float: left to header element, it rises 1cm above the surface and floats towards left boundary of the page. Now, that it's not level with its next element, its next element rises up and towards left as far as it can. But as the floated element has risen only 1cm above, only the bottom wooden board of next element goes up, while all the alphabets get pushed down by the floated element.
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.
If I have a container with an element inside that has a margin, that elements margin will not push it down within that container but instead push down the container itself. That alone, while annoying, might just be how the w3c decided it would work. But the weird thing is, if I add a border to the container that element will now be pushed down within the container and the container will then be flush against the element above (what I want). Until now most of the containers I use have had borders so that I could tell how everything was lining up, but now that I have decided to start adding background-colors and removing margins to make everything look nicer I am running in to a ton of issues with gaps between containers that have neither of the containers background colors applied to them.
In case I was not clear I created a jsfiddle to demonstrate the issue, as you can see by looking at the html / js clicking the button only toggles the existence of a border and nothing else. Yet all the margins between the containers change based on whether or not the border is there.
http://jsfiddle.net/Tysonzero/p5fgjmrn/
HTML:
<div class="first-div">
</div>
<div class="second-div">
<div class="inner-div">Test</div>
</div>
<button onclick="toggle()">Toggle Border</button>
CSS:
*
{
box-sizing: border-box;
margin: 0;
padding: 0;
}
.first-div
{
background-color: red;
min-height: 50px;
}
.second-div
{
background-color: blue;
min-height: 50px;
}
.inner-div
{
margin: 10px;
text-align: center;
}
JS:
toggle = function() {
if (document.getElementsByClassName('second-div')[0].style.border) {
document.getElementsByClassName('second-div')[0].style.border = "";
}
else {
document.getElementsByClassName('second-div')[0].style.border = "1px solid";
}
};
I want it to always work the way it works WITH a border as IMO that makes a ton more sense, and for my project it is what I need. I really hope I don't have to do something hacky such as add a 1px transparent border to everything and compensate for it when necessary. I have tried using overflow: auto; or overflow: hidden. But neither are the same as having a border. overflow: auto; will sometimes create scrollbars if the element inside is bigger than the parent or if you are using negative margins in a certain way whereas adding a border does not, overflow: hidden; completely prevents scrolling whereas adding a border does not.
Does anyone know how I can force the browser to treat everything as though it has a border (or at least make issues like this not happen) and also can anyone explain this behavior? It seems unintentional but I could be wrong.
The specific answer to your question is collapsing margins and is part of the W3C's BOX MODEL specifications:
Vertical margins may collapse between certain boxes:
Two or more adjoining vertical margins of block boxes in the normal flow collapse. The resulting margin width is the maximum of the adjoining margin widths. In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the absolute maximum of the negative adjoining margins is deducted from zero. Note. Adjoining boxes may be generated by elements that are not related as siblings or ancestors.
Vertical margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
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.
Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
Margins of inline-block elements do not collapse (not even with their in-flow children).
If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.
If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
Otherwise, either the element's parent is not taking part in the margin collapsing, or only the parent's bottom margin is involved. The position of the element's top border edge is the same as it would have been if the element had a non-zero bottom border.
An element that has had clearance applied to it never collapses its top margin with its parent block's bottom margin.
Note that the positions of elements that have been collapsed through have no effect on the positions of the other elements with whose margins they are being collapsed; the top border edge position is only required for laying out descendants of these elements.
Margins of the root element's box do not collapse.
So, when you add a border, you're doing exactly what the specification says, therefore you have no margin.
There are many ways and fixes and hacks to solve this, but to me, the most direct and easy is as simple as to apply an overflow:auto; property to the div you want to clear the margin.
Thus, in your CSS, you would only need to change it like this:
.second-div
{
background-color: blue;
min-height: 50px;
overflow:auto;
}
I forked your fiddle with even more border so you can notice the effect and see how good it works
This might be due to margin collapsing and I know about margin collapsing, at least how it affects adjacent elements, but I don't understand how it works on nested elements when negative margins are involved.
For example, in this markup and accompanying CSS:
Markup
<div class="parent">
<div class="child">
Child 1
</div>
</div>
<div class="parent">
<div class="child negative">
Child 1
</div>
</div>
CSS
body {
background: white;
padding: 45px;
}
.parent {
border: 1px solid black;
margin-bottom: 10px;
}
.negative {
margin-bottom: -1px;
}
Live example here.
When I inspect the height of the second .parent div, I notice it is 1 pixel less than the first one. This has happened because of the negative margin on the .negative element inside it. I had a quick look at W3C and couldn't find an explanation for this behavior.
Could someone please explain what's happening here and also provide me with a link to the W3C spec section about it?
This might be due to margin collapsing and I know about margin collapsing, at least how it affects adjacent elements, but I don't understand how it works on nested elements when negative margins are involved.
Section 8.3.1 has all the details. It also covers the behavior of adjoining margins between nested boxes, as well as negative margins.
However, what you're seeing here is not the effect of margin collapse because you have negated it with a border: 1px solid black declaration in your .parent rule. That means having a border there prevents your .parent margin from collapsing with your .child.negative margin altogether.
Rather, this is simply how negative margins work. This is covered in various sections of the visual formatting model, but it's most succinctly and directly addressed in the beginning of Section 11, which summarizes it thus:
Generally, the content of a block box is confined to the content edges of the box. In certain cases, a box may overflow, meaning its content lies partly or entirely outside of the box, e.g.:
...
A descendant box has negative margins, causing it to be positioned partly outside the box.
So what's happening here, instead, is:
The absolute value of the .child.negative element's negative margin is subtracted from the .parent element's actual height (by 1px).
As a result, the .child.negative element itself overflows .parent (because its own height is not changed and the default overflow for any div is visible).
Since margin collapse does not take effect here, the margin-bottom: 10px in your .parent is unaffected. Note that while any subsequent elements in normal flow will be shifted up by 1px, this is mainly due to the negative margin of your .child.negative element; in other words, a side effect of step 1.
And that's all there is to it.
when you are using .negative { margin-bottom: -1px; } then it will moved at the top. see this example.
please refer the following link you understand easily.
http://coding.smashingmagazine.com/2009/07/27/the-definitive-guide-to-using-negative-margins/
body {
background: white;
padding: 45px;
}
.parent {
border: 1px solid black;
margin-bottom: 10px;
min-height: 30px;
}
.negative {
margin-bottom: 20px;
}
conclusion:
For e.g. In your case i have to added min-height:30px to parent class so that it remains fix. if moved only if you add positive margins to negative class. It just because you can see results in above figure that tells all what you need is.
see the cssdesk link click here cssdesk
Hope, it will helps you. Cheers. !!
Margins of first and last elements will apply to outer element when the outer element doesn't have border, padding and content, instead of it self.
In your case, parent node has border, so margin collapsing won't apply in this case. As you have margin-bottom = -1px for the child node inside, when calculate the outer height of the child node will be the height of its content + padding + border-width + margin. So it will be 1px less when measuring from outside. That's why the height of parent node will be 1px less than the upper example. To see it more clearly, you may apply a background to the child node, say yellow, you will find that the child node will overlap the border of the parent node.
But if you remove the border of the parent node, it will be a total different situation.
For instance to explain margin collapsing, say you have
<div style="background-color:black">
<div style="height:10px; background-color:white; margin-top: 10px"></div>
</div>
You will not see a black box of 10px height, as the outer node will be considered to have a 10px margin on top, and the inner one's margin is ignored. And for negative situation, the outer margin will decrease.
Quote from spec:
When two or more margins collapse, the resulting margin width is the maximum of the collapsing margins' widths. In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.
For more info:
https://developer.mozilla.org/en-US/docs/CSS/margin_collapsing
I need two consecutive div elements (with backgrounds) to be touching seamlessly, one below the other. However, this layout breaks when I put a child p element into the bottom div. The margins of the p element force a blank gap between both div elements. This is strange behavior, as I am expecting the margin of the p to stay within the content and background area of the div. It renders the same way on Firefox, Chrome and IE 8.
<div style="background: #ccccff">Top Div</div>
<div style="background: #ffcccc"><p>Bottom Div</p></div>
Here's what it looks like.
I could fix this by changing the margins to paddings for the p element, but then I would also have to do this with header elements, list elements, and any other element I want to use at the start of a div. That is not desirable.
Could someone enlighten me: what caveat of the box model am I missing? Is there an easy way to fix this, preferably by modifying the style of the div?
Add overflow: hidden or overflow: auto to the div
That is the expected behavior*. There are a few ways to get around it. If you float the divs, they will contain the margins of child elements and prevent margin collapsing. Another approach is to add a border or padding to the divs.
* The margins of the div and the p "combine to form a single margin", even though they are nested, because they have adjoining margins with no padding or border between them.
Solution 1
Add overflow: hidden/auto to the containing div to prevent the margin collapsing.
Solution 2
Add positive padding to the containing div and equal negative margin to the inner element
New Solution
Add padding of 0.01px to the containing div, this will prevent the margin collapsing but won't need any negative margin on the inner element.
Setting a positive padding, and a corresponding negative margin on the div element seems to fix the issue, though I know not why.
<div style="background: #ccccff">Top Div</div>
<div style="background: #ffcccc; padding: 1px; margin: -1px"><p>Bottom Div</p></div>