HTML and CSS boggle my mind sometimes.
A DIV with a border shows its background color for the full height of the element and its contents. Why is it that without a border, a DIV will assume (reverse inherit?) its child's margins?
As an example, here is a JSFiddle illustrating the behavior with and without borders.
http://jsfiddle.net/ahNUX
http://jsfiddle.net/ahNUX/1/
Does anyone care to explain how this is a "feature" and not some kind of bug?
Update: adding 1px of padding to the parent is a quick fix.
Sure. In CSS, by default, adjacent top and bottom margins overlap each other. This was a sensible workaround before the adjacent sibling selector (+) was thought of/became well-supported, as it meant that if you wrote h2 {margin-top: 3em;}, you’d have 3ems worth of space above your h2s even if there was a paragraph before it with a bottom margin of 1em.
In your second example, because the <div> doesn’t have any top or bottom padding or borders, its top and bottom margins are adjacent to the <h1>'s default top and bottom margins. Even though the <div>’s margins don’t have any height, they’re still treated as if they exist, and so the <h1>’s margins have to overlap them. As the <div>s margins are by definition outside of the background-color area of the <div>, the <h1>’s margins have to be positioned outside too.
In your first example, because the <div> has a border, its margins are no longer adjacent to the <h1>’s margins, so no overlapping occurs. You can get the same effect by adding top and bottom padding to the <div>: http://jsfiddle.net/ahNUX/7/
(I’m not sure what you mean about the <div> “reverse-inheriting” its child’s padding though. In your examples, neither the <div> nor the <h1> have any padding. The space inside the <div> in your first example is created by the <h1>’s top and bottom margin.)
This is a result of collapsing margins. See http://www.w3.org/TR/CSS2/box.html#collapsing-margins
In a nutshell the top and bottom margin on the <h1> element in your example may have its margin collapse (or overlap) with another element that's above or below it, which would result in a conflict with the background color, so it's not shown. On the other hand, if you have a border, the rules change.
The spec explains it pretty well, albeit in a dry and somewhat technical tone.
Related
This question already has answers here:
CSS margin strange behavior, why?
(3 answers)
Closed 8 years ago.
I don't understand the following behavior:
jsFiddle
If I take a div and I set the top and bottom margins and the div has and height of 0px, only the top margin will be used, but if I set the height of the div to 1px all is working right.
Why is this happening?
Edit
based on the article cited on #Krzysztof Trzos's answer I understood that this behavior is based on
Empty blocks
If there is no border, padding, inline content, height, or min-height to separate a block's margin- top from its margin-bottom, then its top and bottom margins collapse.
And in the same article there is written
Margins of floating and absolutely positioned elements never collapse.
so to prevent this you can set the float:left attribute or overflow:auto
(thx to #SW4 for the second)
Try using padding instead of margin
http://jsfiddle.net/qa011xhu/1/
I thing the thing with the margin is that it's overlapping on itself. Like it would be if there would be two containers one above another and the first one would have margin-bottom: 10px; and 2nd one margin-top: 10px;. There would be space 10px between them, not 20px.
And, found this article:
https://developer.mozilla.org/en-US/docs/Web/CSS/margin_collapsing
Empty blocks
If there is no border, padding, inline content, height, or min-height to separate a block's margin-top from its margin-bottom, then its top and bottom margins collapse.
This is due to margin collapsing
Top and bottom margins of blocks are sometimes combined (collapsed)
into a single margin whose size is the largest of the margins combined
into it, a behavior known as margin collapsing.
You can overcome it by either using padding, or enforcing a new block formatting context on the collapsed element using overflow:auto or overflow:hidden
A block formatting context is a part of a visual CSS rendering of a
Web page. It is the region in which the layout of block boxes occurs
and in which floats interact with each other.
MDN says:
If there is no border, padding, inline content, or clearance to separate the margin-top of a block with the margin-top of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate the margin-bottom of a block with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.
I don't understand the last sentence. Why does the collapsed margin end up outside the parent? If it ends up outside the parent, where will it go? I have searched the web and read several tutorials about margin collapsing, but I didn't find anything about this.
I threw together this little demo to help demonstrate the way this works:
http://jsfiddle.net/9pq8bm0o/
As you can see, I've made three elements, all nested within each other. The 2 inner containers both have a top margin value of 20px, and the outermost container has a top border (one of the things that is considered a margin separator).
So what does this mean?
Because there is no separation at the top of the two child elements, there is only 20px of space in between the outermost container and BOTH of the two child elements... the inner-most child has had it's margin collapse. Conversely, that margin that is there exists within the outermost container simply because of that border.. if you remove the border, all three elements will share the same 20px of margin which will be outside of all three containers.
So why is it like this?
The best way to think about margin collapsing is like this:
Asking for a margin on an element will ensure that it has that much margin at it's top, and nothing more (unless it's forced to have more).. So looking at my example, Does the middle .parent element have 20px of space above it? Yes, it does. Does the innermost child .child have 20px of space above it? Yes, it also does... so the margin rule is being applied correctly. It doesn't matter where that space lives, as long as it is there.
Imagine that there was a border around the .parent element, but the margin was still displayed the way it is without, and then ask those same questions.. Does the .parent element have the space? Yes, but does the .child element? No, it no longer would, because there would not be 20px of space in between it and the border that is now sitting above it... So, in reality, the space does not collapse, so that both of those questions can be answered as a "yes".
I hope that helps.. it's a little less of a direct answer to your question, and more of the theory behind how it works, so to put things a bit more plainly:
tl;dr
Margin, unlike padding, is meant to add space outside of elements. Because of this, margin will always collapse to the highest parent element whenever possible, to ensure that space is always "outside". Because it is outside of the element, it can count towards multiple different elements, as they all share that "outside" space.
According to http://www.maxdesign.com.au/articles/inline/, the section called "Inline elements and padding" says
While padding can be applied to all sides of an inline element, only left and right padding will have an effect on surrounding content.
So in accordance with that, it seems that it is never ever any point to use padding vertical(top,bottom) for inline elements.
Is that correct?
Well, the padding box is the area covered with the background colour, and the border is painted around that, so changing the padding top and bottom can change what the inline element looks like even if it has no effect on the surrounding content.
I've come across a strange behavior related to the margins of a <div> element. I've added a very simple test case to jsFiddle to show you what I mean.
Interestingly the margins are used correctly when using a border. Is this the default behavior? Does this have something to do with collapsing margins? Is it possible to somehow force the margins without any hacks?
Least ugly solution so far that I thought of:
.cssContainer {
margin:10px;
padding:0.1px;
background-color:#FF0000;
}
But I don't know whether this will work in every browser.
This is another case of adding overflow:auto to the rescue!
jsFiddle example
Just add overflow:auto to your container div and voila, harmony is restored.
.cssContainer {
margin:10px;
padding:0px;
overflow:auto;
background-color:#FF0000;
}
From specs, the reason this works is because a new block formatting context is established by applying overflow other than visible, forcing margins of .cssContainer to not collapse with its in-flow children .cssElement.
From this site
the simplest way to stop the margin collapse from occurring is to add
padding or borders to each element. If we wanted 10px margins on each
element we could simply use a 9px margin and 1px of padding to get the
result we wanted
This is due to the CSS box model. The elements with the red backgrounds will not grow in height to accommodate the top and bottom padding of its children elements, but because the border was specified in the second example and encompasses the padding, the padding will show.
The overflow: auto; property works because the padding overflows its parent element. To illustrate this, in Google Chrome Developer Tools, find and click the HTML row for the green element whose bottom padding will not show. Chrome will highlight the element on the page. Notice how the padding is included in the highlighted element. The padding actually overflows the parent element. The parent element won't show it though because there's no content in or after the padding (no 'bounds').
From the css spec regarding box models:
Adjoining vertical margins collapse, except:
...
Horizontal margins never collapse.
Two margins are adjoining if and only if:
...
both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
top margin of a box and top margin of its first in-flow child
bottom margin of box and top margin of its next in-flow following sibling
bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
...
Note the above rules imply 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.
Unfortunately, text-overflow: auto is slightly risky because the spec says:
auto
The behavior of the 'auto' value is user agent-dependent,
but should cause a scrolling mechanism to be provided for overflowing boxes
However, all browsers that I have looked at appear to implement it in the following manner:
The box is expanded to the smaller amount between:
as much as its containing box allows
as much as is necessary to display the content without clipping
Then if clipping must occur, a scrollbar is added.
This is my code :
HTML
<div class='father'>
<div class='son'>Son</div>
</div>
CSS
.father
{
background-color:blue;
}
.son
{
margin-top:50px;
background-color:red;
height:50px;
}
Where is the background-color blue of the father?
I know how to fix this problem (putting padding-top:1px to the father) but I'd like to know why of this behaviour!
For me it doesnt make sense, because on every browsers, not only IE, this is the behaviour.
This is a result of Collapsing Margins. You can read a good article by Eric Meyer on this topic where he illustrates this exact behavior. The following image is from his article.
Here's what the CSS2 specification has to say about it.
8.3.1 Collapsing margins
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.
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.
Source: http://www.w3.org/TR/CSS2/box.html#collapsing-margins
This is because the div is a block-level element. Certain block-level elements don't contain any padding by default. Divs are one such element. Block-level elements will take up the entire height and width of the container while respecting any padding it may contain.
W3C Visual formatting model
The parent element's height is set to its content's height by default. Once you set a height on the parent, that's no longer the default. Checking up on it, I believe that the padding adds to the height. So, the height is originally determined by the content unless otherwise stated in the CSS. Then, in most cases (IE 6 may be the exception), the padding is added to the height.
Good thing about SO, it helps us be much more detailed in our responses. :)
In CSS, block level elements naturally fills the elements content area, so your "son" div is filling your "father" div completely. Of course, you can bypass this by adding margin/padding/height to your parent div.
You're setting blue explicitly:
.father {
background-color:blue;
}
It's overridden (thanks to the "C" in CSS a.k.a cascading) but the style remains on your parent element (here, appropriately named "father").
If your .father box receives any height at all (check in the Firebug/Chrome dev tools inspector) than the blue is going to show through. I am guessing this is what you're seeing in IE (that or perhaps there's a flash of content before your child element style comes in). I don't think the IE debug tools show bounding boxes but you can test the element for it's height using JavaScript.
Moving outwards from the content, you have padding, border, and margin (as you probably know). Background covers only padding, but not margin.