ChatGPT解决这个技术问题 Extra ChatGPT

Margin on child element moves parent element

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.

this behavior makes no sense what so ever. Margin is suppose to stay inside the parent. Not move the parent. Who writes these rules.
+2 for last comment. seriously this rule bugs me. "60% of the time it works every time" - that's margins.
I totally agree with the two last commenters. This is insane. What's interesting is that adding a 1px border to the parent makes it work right, however this means you have a border...if this is the expected behavior then this is ridiculous
This may also help you :) stackoverflow.com/questions/35337043/…
This reminds me of the default box-sizing rule: "We need to ship a box. The box must not be larger than 100cm. We need 10cm of padding inside the box to ensure our content doesn't break during the shipping. Let's make the box 120cm!" What a joke.

v
vdboor

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;
}

2021 update: if you're willing to drop IE11 support you can also use the new CSS construct display: flow-root. See MDN Web Docs for the whole details on block formatting contexts.

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

h1, h2, p, ul { margin-top: 1em; margin-bottom: 1em; outline: 1px dashed blue; } div { outline: 1px solid red; }

Title!

Title!

Paragraph

Title!

Paragraph

  • list item

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.


i wanna know why does this happen what good this does to anyone. What situation this 'feature' was meant to be.
@MuhammadUmer, well this has to do with text content. For example, a series of <h2>, <p>, <ul> won't get strange gaps or "double" margins this way.
Could you give some example. Let say i have div as background. With h1, h2 and p in it. Now i want to move h1 down a bit so it's not so near to the above edge of background div, yet not expand the h1 element itself. I'd think add top margin. Which is obvious. but when i do that, the glorious background decides to move down with it. How this is a feature. How this helps in spacing between h1,h2, and p. Help me see what you are saying.
I've added an example for you
i would expect it to add in more margin. collapsing margins is by far the most annoying thing in my development career. if i put 2 divs next to each other with margin: 5px 10px; id expect 2 divs 5 px from the top and 20px between them not 5 px from the top and 10px between them. if i wanted 10 px between them id have specified margin: 5px 5px;. i honestly wish there was a root rule i could place that would stop all margin collapsing. i hate that i have to double the size of the margins on paper to get them to actually do what the heck i want on screen. and this top thing... what a disaster
B
Ben James

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;
}

Adding border to the parent will effect the child's margin too, try .parent {border: solid 1px;}
The margin doesn't add to the padding... it applies separately. But the visual effect is the same.
What if you want a top-margin…? Not really a solution.
Like feeela said - this isn't really a solution if a top margin is required. vdboor's answer suits better.
There are bunch of solutions, you just need to be creative enough. Cheers. :-)
E
Ejaz

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/


margin-top: -1px seems to be unnecessary. But I like this.
Actually, both margin-top: -1px and height: 0 seems unnecessary. Tested in Chrome. But best solution.
This method mostly works, but will not respect the bottom margin for the last element within the container. It is not an equal fix to overflow: hidden; for example.
@MichaelGiovanniPumo the answer can be modified to work with bottom margin of last element by using .parent:after...
This is most definitely the correct answer nowadays. Works flawlessly (put it on both after and before), I hope people dare to scroll past the other ones!
C
Code Lღver

add style display:inline-block to child element


G
George SEDRA

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


yes, sometimes only this can help... or better put something like this:
 
e
erdomester

This is what worked for me

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

.child {
margin-top:260px;
}

http://jsfiddle.net/97fzwuxh/


b
bingo

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

Float

Padding

Border

Overflow


Y
Yeti

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>

Only use this solution if you are desperate, and you can't set any custom CSS to the existing elements - otherwise use the real solution: set overflow: hidden; for the parent.
v
vincent thorpe

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.


H
Hyzyr

Play with display of parent

.parent{
     display: inline-block;
     width: 100%;
}

or

.parent{ display: flex; }

2
25thhour

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.


Z
Zyl

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.


s
schellmax

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;


S
SandRock

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;
}

l
lamplightdev

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


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now