This question already has answers here:
Chrome / Safari not filling 100% height of flex parent
(5 answers)
Closed 6 years ago.
I found if we set a block level element with height:auto or height: 0~100% without set up parent's height with explicitly value, and its block level child has bottom margin, then it will calculate height differently in Chrome, but not in Firefox. For the case which set height: 1%:
http://codepen.io/anon/pen/BjgKMR
html {
background: pink;
}
body {
background: yellow;
}
div {
height: 1%;
}
inner {
margin-bottom: 30px;
margin-top: 20px;
}
<div>
<p class="inner">block level element</p>
</div>
The height of div block will be calculated as the margin-bottom + content height of p element. I am confused about why the height: 1% should be computed as to auto because the parent elements(html and body tag) not set its height explicitly, but has different height as we just directly set the height to auto?
If we directly set it to height: auto, it will clearly just set the height as its child block-level element's height, which is not include its bottom margin.
html {
background: pink;
}
body {
background: yellow;
}
div {
height: auto;
}
inner {
margin-bottom: 30px;
margin-top: 20px;
}
<div><p class="inner">block level element</p></div>
I have read the CSS 2.1 spec and think about my question might covered with the height property and margin collapse topic, but still cannot understand why it behaves different in Chrome ver. 47.0.2526, though Firefox ver. 44.0.2 will display the height with same value.
Listed references:
https://www.w3.org/TR/CSS2/visudet.html#the-height-property
10.5: percentage
... If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'. ...
10.6.3: Block-level non-replaced elements in normal flow when overflow computes to visible.
... if 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
The element's height is the distance from its top content edge to the first applicable of the following:
the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin does not collapse with the element's bottom margin
the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
zero, otherwise
https://www.w3.org/TR/2011/REC-CSS2-20110607/box.html#collapsing-margins
8.3.1 collapsing margins.
The top margin of an in-flow block element collapses with its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.
The bottom margin of an in-flow block box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.
... 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.
So first you have the W3C standards, which are a set of guidelines for browser makers.
And then you have the browser makers, who are free to do whatever they want (as evidenced by a history of deviations by Internet Explorer).
In particular, with CSS percentage heights, there are clear differences in behavior among browsers.
You've posted one example. Here's another:
Percentage Heights in Flexbox: Chrome/Safari vs Firefox/IE
When working with flexbox, Chrome and Safari resolve percentage heights on flex items based on the value of the parent's height property. Firefox and IE11/Edge prioritize the parent's flex height.
It appears Webkit browsers adhere to a more traditional interpretation of the spec:
CSS height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly and this element is not absolutely positioned, the value computes to "auto".
auto The height depends on the values of other properties.
In other words, for percentage height to work on an in-flow child, the parent must have a set height.
That is the traditional interpretation of the spec: The term "height" means the value of the height property. My own view is that this language is vague and open to interpretation, but the height property requirement has become the predominant implementation. I've never seen min-height or max-height work on a parent when dealing with percentage values.
Recently, however, Firefox and IE have broadened their interpretation to accept flex heights, as well.
Examples of Firefox and IE using a parent's flex height as reference for a child's percentage height:
Chrome ignoring flex-basis in column layout
Chrome / Safari not filling 100% height of flex parent
Height is not correct in flexbox items in Chrome
Flexbox in Chrome--How to limit size of nested elements?
Knowing which browsers are in compliance with the spec is a bit difficult because, as I mentioned before, the spec language seems vague and open to interpretation.
With the last update to this part of the definition being in 1998 (CSS2), and the advent of new forms of height such as flex height, an update seems long overdue.
I think it's fair to say that when it comes to percentage heights, until the spec definition gets an update, you can expect rendering differences among browsers.
Alternative Solutions
Here are two alternatives to consider when wanting a child element to take the parent's full height.
Apply display: flex to the parent. This automatically sets align-items: stretch, which tells the child to expand the full available height of the parent.
Apply position: relative on the parent and position: absolute; height: 100%; width: 100% on the child. With absolute positioning, a percentage height will work without a specified height on the parent.
Related
I'm in a confusion since yesterday. Actually I have a markup as follows:
<div class="container">
<div class="child>
<div class="descendant">Content</div>
</div>
</div>
And CSS:
.container {
max-width: 500px;
min-width: 100px;
}
The .container element has some width between 100px to 500px according to my content and the child & descendant elements have some other content without any specific width or min/max-width.
What happens is that when I apply horizontal padding to .container, its content-box size remains as is & just padding is added to it. But whenever I apply horizontal padding to any of the child or descendant elements, they do not add padding to their size or the parent's size. Instead, their content-box size reduces and they accommodate padding.
I seriously can't get what is going on here. Can anyone explain me what's actually happening?
The width describes the content width, which you explicitly set to a maximum of 500px. So long as there is room, the content width of the container will be 500px. If you add padding to the container, that doesn't affect the content width because nothing else constrains it.
The descendants, on the other hand, have their width constrained by the container. If you add padding to a descendants, the width is reduced so the whole box can still fit inside teh container.
This is just normal box model behavior. Adding padding to an element that isn't bound by a parent element restricting it's size will grow outward to accommodate the new padding. But if an element is contained within a parent with a fixed width, and you add padding to the inner element, it can't grow outward and force the bounding parent to grow - it can only reduce it's content-box to make room for the padding.
If you want the outer and inner divs to behave consistently, you can add box-sizing: border-box; to .container and the padding will not cause the element to grow beyond the specified width/height.
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 question is similar to this one (with an excellent answer), although mine doesn't pertain to float issues.
I recently ran into some trouble when trying to apply a margin to an only-child of a block-level element:
#parent {
background: rgba(255, 0, 0, 0.1);
}
#child {
margin: 30px 0;
padding: 20px;
background: rgba(0, 255, 0, 0.1);
}
<div id="parent">
<div id="child">Foo</div>
</div>
Although the margin is applied, the parent's background is not. This remains true unless siblings are added before and after the #child, or (more interestingly in my opinion), an overflow of any value other than visible is set. Here is the same example but with an overflow value:
#parent {
background: rgba(255, 0, 0, 0.1);
overflow: auto;
}
#child {
margin: 30px 0;
padding: 20px;
background: rgba(0, 255, 0, 0.1);
}
<div id="parent">
<div id="child">Foo</div>
</div>
From CSS2.1 Section 9.4.1 - Block Formatting Contexts, I found the following:
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.
I'm really struggling to understand the rationale behind the "overflow other than visible" logic in this instance. The margins are seemingly not being clipped in this situation, as the only thing to change is the background. Could someone demonstrate why a value of overflow: visible creates such a situation?
As I have covered in my answer to the question that you link to, the main reason overflow values other than visible result in a new block formatting context is due to implementation limitations relating to floats, even though the concept of overflow does not immediately appear to have a relationship with floats.
While the relationship between floats and collapsing margins is pretty simple (it never occurs), the fact that margins cannot collapse through the boundaries of an element with such a value for overflow either is little more than a side effect of this change, because margins are defined not to collapse through any box that establishes a block formatting context, as described in section 8.3.1. I quote:
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.
This includes both floats and elements with such a value for overflow. The overflow itself does not actually have any direct effect on the margins.
When both the parent and the child are block-level elements that participate in the same block formatting context, they will collapse by default unless there is something in the way:
The top margin of an in-flow block element collapses with its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.
The bottom margin of an in-flow block box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.
This explains why the parent's background does not extend until you try and block the margin collapse.
In your first examples the top and bottom margins of parent and child are collapsed. Roughly speaking, the 30px margin of child is combined with the zero margin of parent, the greater margin of the two is applied on the parent. Quote:
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.
In the same document, the effect of overflow (as in your second example) is explained:
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.
This clarifies an issue I've occasionally had with heights. It looks like the height of the parent element is not being calculated from the margins of the child.
Adjusting your fiddle: http://jsfiddle.net/u6tQj/2/
#parent { background: #555;}
#child { margin:200px; background: #ccc; }
The margins are being applied to the child, but the height of the parent was unaffected. Adding overflow or border causes the height to adjust. I do sometimes struggle with why parent elements don't always expand to contain the children.
It seems very odd, but...
It could be complicated to have conflicting rules on parent height. It would affect many of the browser layouts, such as parent position relative with children set to absolute, or overflow auto/hidden, and probably a host of other layout algorithms. I imagine it would unnecessarily complicate the browser's layout engine, in the same way that css selector specificity can sometimes be a major pain for us.
You would add padding to the parent to adjust its height, which is more intuitive (isn't it?).
Previously my assumption about width: auto was that the width is set to that of the contents. Now I see that it takes the full width of the parent.
Can anyone please describe the differences between these?
Width auto
The initial width of a block level element like div or p is auto. This makes it expand to occupy all available horizontal space within its containing block. If it has any horizontal padding or border, the widths of those do not add to the total width of the element.
Width 100%
On the other hand, if you specify width:100%, the element’s total width will be 100% of its containing block plus any horizontal margin, padding and border (unless you’ve used box-sizing:border-box, in which case only margins are added to the 100% to change how its total width is calculated). This may be what you want, but most likely it isn’t.
To visualise the difference see this picture:
Source
width: auto; will try as hard as possible to keep an element the same width as its parent container when additional space is added from margins, padding, or borders.
width: 100%; will make the element as wide as the parent container. Extra spacing will be added to the element's size without regards to the parent. This typically causes problems.
Width 100% :
It will make content width 100%. margin, border, padding will be added to this width and element will overflow if any of these added.
Width auto :
It will fit the element in available space including margin, border and padding. space remaining after adjusting margin + padding + border will be available width/ height.
Width 100% + box-sizing: border box :
It will also fits the element in available space including border, padding (margin will make it overflow the container).
It's about margins and border. If you use width: auto, then add border, your div won't become bigger than its container. On the other hand, if you use width: 100% and some border, the element's width will be 100% + border or margin. For more info see this.
As long as the value of width is auto, the element can have horizontal margin, padding and border without becoming wider than its container (unless of course the sum of margin-left + border-left-width + padding-left + padding-right + border-right-width + margin-right is larger than the container). The width of its content box will be whatever is left when the margin, padding and border have been subtracted from the container’s width.
On the other hand, if you specify width:100%, the element’s total width will be 100% of its containing block plus any horizontal margin, padding and border (unless you’ve used box-sizing:border-box, in which case only margins are added to the 100% to change how its total width is calculated). This may be what you want, but most likely it isn’t.
Source:
http://www.456bereastreet.com/archive/201112/the_difference_between_widthauto_and_width100/
The initial width of a block level element like div or p is auto.
Use width:auto to undo explicitly specified widths.
if you specify width:100%, the element’s total width will be 100% of its containing block plus any horizontal margin, padding and border.
So, next time you find yourself setting the width of a block level element to 100% to make it occupy all available width, consider if what you really want is setting it to auto.
When we say
width:auto;
width will never exceed the total width of parent element. Maximum width is it's parent width. Even if we add border, padding and margin, content of element itself will become smaller in order to give space for border, padding and margin. In case if space required for border + padding + margin is greater than total width of parent element then width of content will become zero.
When we say
width:100%;
width of content of element will become 100% of parent element and from now if we add border, padding or margin then it will cause child element to exceed parent element's width and it will starts overflowing out of parent element.
For positioning elements,
width: 100%, not relative to the parent, but the nearest positioned element.
If they are all statically positioned, it is the nearest parent element.
Using width:auto; + display:inline-block; in css giving awesome effect.
width : auto; makes element width auto for adjustment with another object using with display: inline-block; like if we have a div element and another one also div element and div elements are block level element so showing them together in one line use width: auto; and display:inline-block
This question already has answers here:
Why doesn't a child's margin affect a parent's height?
(2 answers)
Closed 10 months ago.
I have come across a case where I need a child's margin to expand a parent container. I found that the space outside of the parent is allocated, but the parent itself is not expanded. I then found that by adding overflow: hidden to the parent I could fix this issue.
Can anyone shed any light on why this is the case?
I have found that adding any padding or border value to the parent also fixes this.
section {
background: black;
//overflow: hidden; /* toggle this */
//padding: 1px; /* or toggle this */
//border: solid 1px green; /* or toggle this */
}
div {
margin: 10px;
background: red;
}
<section>
<div>
SOME CONTENT
</div>
</section>
JSFiddle Example
The answer to "why" is described well and succinctly here. There are certain properties that establish a "block formatting context". Namely:
Floats, absolutely [and fixed] 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.
It is this change of block formatting context that is the reason why such solutions as given above in the comments work for how margin (and in the case of a preceding float, padding of following inflow elements) operates.
I think collapsing margins is the reason : http://www.w3.org/TR/CSS2/box.html#collapsing-margins
A small update as of July 2018. Mozilla has a great article on this matter which is due to margin collapse.
Summary: Margins of the parent and the first child may collapse. They end up as if the parent had the resulting margin.
Parent and first/last child
If there is no border, padding, inline part, block formatting context created, or clearance to separate the margin-top of a block from 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 from the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.