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>
Related
The definition of overflow:hidden states that :
the overflowing content is completely hidden, not accessible to the user.
from: http://quirksmode.org/css/css2/overflow.html
But i am curious about this behavior from my js fiddle:
https://jsfiddle.net/gd62qmr3/2/
One thing i noticed is that, it gave color to the margins after setting the overflow to hidden?
<div style="background-color:green;overflow:hidden;">
<p>test</p>
</div>
I wanna know why did such behavior occur? the exact block element will have the green background color if there is no overflow but putting overflow will give background color to its margins.
That's because overflow: hidden affects margin collapse.
p elements have some vertical margin by default. According to the spec, it collapses with the margin of the parent div:
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.
Adjoining vertical margins collapse, except [... not
relevant]
[... The] top margin of a box and top margin of its first in-flow
child [are adjoining]
However, overflow: hidden prevents that:
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.
Your color is set to the parent of your paragraph. You have nothing stopping the height of your parent div to expand as far as it wants, so the paragraph margins are making the parent height larger.
If you were to give a set height to your parent (18px or so) then it will hide the margins (and text technically)
div {
height:18px;
}
https://jsfiddle.net/gd62qmr3/3/
If you were to set the color to your paragraph, then you will not see the background color in the margins
div {
overflow:hidden;
}
div p {
background-color:green;
margin:20px 0;
}
https://jsfiddle.net/gd62qmr3/4/
The definition of overflow:hidden states that :
the overflowing content is completely hidden, not accessible to the user.
from: http://quirksmode.org/css/css2/overflow.html
But i am curious about this behavior from my js fiddle:
https://jsfiddle.net/gd62qmr3/2/
One thing i noticed is that, it gave color to the margins after setting the overflow to hidden?
<div style="background-color:green;overflow:hidden;">
<p>test</p>
</div>
I wanna know why did such behavior occur? the exact block element will have the green background color if there is no overflow but putting overflow will give background color to its margins.
That's because overflow: hidden affects margin collapse.
p elements have some vertical margin by default. According to the spec, it collapses with the margin of the parent div:
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.
Adjoining vertical margins collapse, except [... not
relevant]
[... The] top margin of a box and top margin of its first in-flow
child [are adjoining]
However, overflow: hidden prevents that:
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.
Your color is set to the parent of your paragraph. You have nothing stopping the height of your parent div to expand as far as it wants, so the paragraph margins are making the parent height larger.
If you were to give a set height to your parent (18px or so) then it will hide the margins (and text technically)
div {
height:18px;
}
https://jsfiddle.net/gd62qmr3/3/
If you were to set the color to your paragraph, then you will not see the background color in the margins
div {
overflow:hidden;
}
div p {
background-color:green;
margin:20px 0;
}
https://jsfiddle.net/gd62qmr3/4/
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
Given the following markup:
<div>
<p>Test test</p>
</div>
Setting the margin: 10px to the paragraph element should apply to both top and bottom, correct?
I have a simple example, and I don't quite know why it doesn't work.
http://jsbin.com/URumOFup/2/edit?html,css,output
This works as I expect it to if I give the div the rule of overflow: hidden. Can someone explain why this happens and if using overflow: hidden is the proper way to have the margins of p apply?
The general meaning of "margin" isn't to convey "move this over by 10px" but rather, "there must be 10px of empty space beside this element."
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.
Adjoining vertical margins collapse, except:
Margins of the root element's box do not collapse.
If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.
Since your outter DIV is not 'floated' .. top margin of inner element <p> will collapse to 0 for the first one, then collapse to a minimum spacing for other <p> tags.
Just 'float:left;width:100%' your div will solve your issue.
Margin-collapsing w3c reference
There is a block:
<div class="block">
<p>this is a paragraph this is a paragraph</p>
</div>
And its style:
.block{background:gray; width:300px}
p{margin-top:50px}
This is the result:
Well, but when I add padding-top to the .block, the result differs:
.block{background: gray; width:300px; padding-top:1px}
p{margin-top:50px;}
The result:
It looks that by adding padding to the parent, the margin of the child (p) caused expanding.
You could checkout the fiddles: http://jsfiddle.net/YFU2f/2/ and http://jsfiddle.net/YFU2f/3/
By default, adjacent margins collapse, when you introduce a padding between them they aren't anymore adjacent, so they become separate.
In your example, the top margin you defined on the p element collapses with the top margin of the body element (which is usually 8px by default), so at the end, the distance between the top of the canvas and the top of the content box of p is 50px. If you introduce the padding, it becomes 8px+1px+50px=59px. You can also notice the collapse of the margin by looking at the background of the div: when the margin collapses, it is as if the 50px margin was only declared for the body.
You can find all the details in the specifications: http://www.w3.org/TR/CSS21/box.html#collapsing-margins
you need to use display p as block
p{margin-top:50px;display:inline-block;}