Margin on child element moves parent element

Robert Koritnik Source

I have a div (parent) that contains another div (child). Parent is the first element in body with no particular CSS style. When I set

.child
{
    margin-top: 10px;
}

The end result is that top of my child is still aligned with parent. Instead of child being shifted for 10px downwards, my parent moves 10px down.

My DOCTYPE is set to XHTML Transitional.

What am I missing here?

edit 1
My parent needs to have strictly defined dimensions because it has a background that has to be displayed under it from top to bottom (pixel perfect). So setting vertical margins on it is a no go.

edit 2
This behaviour is the same on FF, IE as well as CR.

cssxhtmlmarginnested

Answers

answered 9 years ago Ben James #1

This is normal behaviour (among browser implementations at least). Margin does not affect the child's position in relation to its parent, unless the parent has padding, in which case most browsers will then add the child's margin to the parent's padding.

To get the behaviour you want, you need:

.child {
    margin-top: 0;
}

.parent {
    padding-top: 10px;
}

answered 9 years ago 25thhour #2

The margin of the elements contained within .child are collapsing.

<html>
<style type="text/css" media="screen">
    #parent {background:#dadada;}
    #child {background:red; margin-top:17px;}
</style>
<body>
<div id="parent">

    <p>&amp;</p>

    <div id="child">
        <p>&amp;</p>    
    </div>

</div>
</body>
</html>

In this example, p is receiving a margin from the browser default styles. Browser default font-size is typically 16px. By having a margin-top of more than 16px on #child you start to notice it's position move.

answered 9 years ago vdboor #3

Found an alternative at Child elements with margins within DIVs You can also add:

.parent { overflow: auto; }

or:

.parent { overflow: hidden; }

This prevents the margins to collapse. Border and padding do the same. Hence, you can also use the following to prevent a top-margin collapse:

.parent {
    padding-top: 1px;
    margin-top: -1px;
}

Update by popular request: The whole point of collapsing margins is handling textual content. For example:

<style type="text/css">
    h1, h2, p, ul {
        margin-top: 1em;
        margin-bottom: 1em;
    }
</style>

<h1>Title!</h1>
<div class="text">
    <h2>Title!</h2>
    <p>Paragraph</p>
</div>
<div class="text">
    <h2>Title!</h2>
    <p>Paragraph</p>
    <ul>
        <li>list item</li>
    </ul>
</div>

Because the browser collapses margins, the text would appear as you'd expect, and the <div> wrapper tags don't influence the margins. Each element ensures it has spacing around it, but spacing won't be doubled. The margins of the <h2> and <p> won't add up, but slide into each other (they collapse). The same happens for the <p> and <ul> element.

Sadly, with modern designs this idea can bite you when you explicitly want a container. This is called a new block formatting context in CSS speak. The overflow or margin trick will give you that.

answered 5 years ago Zyl #4

I had this problem too but preferred to prevent negative margins hacks, so I put a

<div class="supercontainer"></div>

around it all which has paddings instead of margins. Of course this means more divitis but it's probably the cleanest way to do get this done properly.

answered 5 years ago George SEDRA #5

the parent element has not to be empty at least put &nbsp; before the child element.

answered 4 years ago schellmax #6

interestingly my favorite solution to this problem isn't yet mentioned here: using floats.

html:

<div class="parent">
    <div class="child"></div>
</div>

css:

.parent{width:100px; height:100px;}
.child{float:left; margin-top:20px; width:50px; height:50px;}

see it here: http://codepen.io/anon/pen/Iphol

note that in case you need dynamic height on the parent, it also has to float, so simply replace height:100px; by float:left;

answered 4 years ago erdomester #7

This is what worked for me

.parent {
padding-top: 1px;
margin-top: -1px;
}

.child {
margin-top:260px;
}

http://jsfiddle.net/97fzwuxh/

answered 4 years ago lamplightdev #8

Using top instead of margin-top is another possible solution, if appropriate.

answered 4 years ago SandRock #9

An alternative solution I found before I knew the correct answer was to add a transparent border to the parent element.

Your box will use extra pixels though...

.parent {
    border:1px solid transparent;
}

answered 3 years ago Атанас Димитров #10

add style display:inline-block to child element

answered 3 years ago Ejaz #11

Although all of the answers fix the issue but they come with trade-offs/adjustments/compromises like

  • floats, You have to float elements
  • border-top, This pushes the parent at least 1px downwards which should then be adjusted with introducing -1px margin to the parent element itself. This can create problems when parent already has margin-top in relative units.
  • padding-top, same effect as using border-top
  • overflow: hidden, Can't be used when parent should display overflowing content, like a drop down menu
  • overflow: auto, Introduces scrollbars for parent element that has (intentionally) overflowing content (like shadows or tool tip's triangle)

The issue can be resolved by using CSS3 pseudo elements as follows

.parent::before {
  clear: both;
  content: "";
  display: table;
  margin-top: -1px;
  height: 0;
}

https://jsfiddle.net/hLgbyax5/1/

answered 2 years ago vincent thorpe #12

I find out that, inside of your .css >if you set the display property of a div element to inline-block it fixes the problem. and margin will work as is expected.

answered 8 months ago Yeti #13

Neat CSS-only solution

Use the following code to prepend a contentless first-child to the unintentionally moving div:

.parent:before
{content: '';position: relative;height: 0px;width: 0px;overflow: hidden;white-space: pre;}

The advantage of this method is that you do not need to change the CSS of any existing element, and therefore has minimal impact on design. Next to this, the element that is added is a pseudo-element, which is not in the DOM-tree.

Support for pseudo-elements is wide-spread: Firefox 3+, Safari 3+, Chrome 3+, Opera 10+, and IE 8+. This will work in any modern browser (be careful with the newer ::before, which is not supported in IE8).

Context

If the first child of an element has a margin-top, the parent will adjust its position as a way of collapsing redundant margins. Why? It's just like that.

Given the following problem:

<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}
</style>

<div class="parent"><!--This div moves 40px too-->
    <div class="child">Hello world!</div>
</div>

You can fix it by adding a child with content, such as a simple space. But we all hate to add spaces for what is a design-only issue. Therefore, use the white-space property to fake content.

<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}
.fix {position: relative;white-space: pre;height: 0px;width: 0px;overflow: hidden;}
</style>

<div class="parent"><!--This div won't move anymore-->
    <div class="fix"></div>
    <div class="child">Hello world!</div>
</div>

Where position: relative; ensures correct positioning of the fix. And white-space: pre; makes you not having to add any content - like a white space - to the fix. And height: 0px;width: 0px;overflow: hidden; makes sure you'll never see the fix.

You might need to add line-height: 0px; or max-height: 0px; to ensure the height is actually zero in ancient IE browsers (I'm unsure). And optionally you could add <!--dummy--> to it in old IE browsers, if it does not work.

In short, you can do all this with only CSS (which removes the need to add an actual child to the HTML DOM-tree):

<style type="text/css">
div {position: relative;}
.parent {background-color: #ccc;}
.child {margin-top: 40px;}

.parent:before {content: '';position: relative;height: 0px;width: 0px;overflow: hidden;white-space: pre;}
</style>

<div class="parent"><!--This div won't move anymore-->
    <div class="child">Hello world!</div>
</div>

answered 4 months ago bingo #14

To prevent "Div parent" use margin of "div child":
In parent use these css:

  • Float
  • Padding
  • Border
  • Overflow

comments powered by Disqus