2

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.

semicolon
  • 2,530
  • 27
  • 37
  • I guess it is about collapsing margins, when no border is there! – loveNoHate Oct 02 '14 at 05:04
  • I'm not sure why you are using the inner div to add the margin. I would add the margin to .second-div if that works with you. see http://jsfiddle.net/AuroraArcade/p5fgjmrn/2/ – Aurora Arcade Oct 02 '14 at 05:10
  • I want the two containers to be flush. I just want the element to be separated from the top of the container. And as for different elements but with the same container I want different margins I cannot just put padding on the second container. – semicolon Oct 02 '14 at 16:24

1 Answers1

2

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

Devin
  • 7,690
  • 6
  • 39
  • 54
  • The main issue I have with overflow: auto; is that it has on many occasions created unwanted scrollbars. Such as when I use negative margins or specify a fixed height that is supposed to be the same as the containing elements but varies slightly on different browsers. Is there a border: 0px solid; type of thing that would work? – semicolon Oct 02 '14 at 16:29
  • not following you. You can add 1 million words to that div and it won't have any scroll bar, try it and see by yourself. To have a scrollbar, you would need to define sizes, which would be a different case. Also, I'm answering your specific question (why) and offering the solution (how) and you're mentioning the border thing but now you ask for the border again, so you are confusing me. This being said, a border 0 is impossible because it's the same as border:none, so the best you can do if you want to use border (and can't imagine a reason) is to use `calc` property and take 1px off the size – Devin Oct 02 '14 at 17:55
  • I often get the scroll bar if I use negative margins. I used to do `*{overflow:auto;}` but scroll bars appeared where I used negative margins. Your explanation and solution is great, thank you! I was just wondering if there was a solution that wouldn't cause scroll bars when using negative margins. `border: 0 solid` if it solved this would have been awesome as it would not have affected the size, but it looks like I'm just going to have to work around the scrollbars and the like. – semicolon Oct 02 '14 at 21:53
  • Isn't this more clear: "the expression collapsing margins means that adjoining margins of two or more boxes combine to form a single margin" - from w3.org removed parentheses. – Valter Ekholm Jan 31 '21 at 19:46
  • @ValterEkholm this answer has 7 years, what I answered is teh way it was 7 years ago, opied and pasted from teh source – Devin Feb 01 '21 at 16:34
  • Yes... it was good, but I found the cited text by following the link you gave, – Valter Ekholm Feb 03 '21 at 07:32