144

I have a header div as the first element in my wrapper div, but when I add a top margin to a h1 inside the header div it pushes the entire header div down. I realize this happens whenever I apply a top margin to the first visible element on a page.

Here is a sample code snippet. Thanks!

div#header{
 width: 100%;
 background-color: #eee;
 position: relative;
}

div#header h1{
 text-align: center;
 width: 375px;
 height: 50px;
 margin: 50px auto;
 font-size: 220%;
 background: url('../../images/name_logo.png') no-repeat;
}
<div id="header">
 <h1>Title</h1>
 <ul id="navbar"></ul>
</div>
LiuYan 刘研
  • 1,614
  • 1
  • 16
  • 29
Danny
  • 4,724
  • 6
  • 42
  • 55

8 Answers8

214

put overflow:auto in the parent div
see more in this link

Community
  • 1
  • 1
JuanPablo
  • 23,792
  • 39
  • 118
  • 164
  • 31
    `overflow:hidden` is better for 90% of the cases. – vsync Apr 08 '13 at 14:48
  • 1
    Why would overflow:hidden be better? – peterchon Oct 25 '13 at 18:49
  • 5
    @peterchon - because auto can cause scrollbars – vsync Feb 23 '14 at 15:12
  • 3
    does not work anymore, the answer by @SW4 in this [link](http://stackoverflow.com/questions/23788169/why-is-setting-the-top-margin-of-this-child-element-pushing-its-parent-container) is better – am05mhz Nov 16 '15 at 04:07
  • 1
    `overflow: hidden` or `overflow: auto` can cause unwanted side effects, some of which you might not see at first sight. I've found that changing `margin` to `padding` does the trick, as the answer below suggests. Still don't understand why the whole problem happens, though. – Gilad Barner May 03 '17 at 13:38
  • Can somebody please explain why this happens? – George Jul 17 '17 at 17:38
  • 1
    @GiladBarner "overflow: hidden or overflow: auto can cause unwanted side effects, some of which you might not see at first sight" → What unwanted side effects? Please elaborate! – mesqueeb Mar 26 '18 at 07:48
37

I don't have a solid explanation on why this happens, but I've fixed this by changing the top-margin to top-padding, or adding display: inline-block to the element style.

EDIT: This is my theory

I have a feeling it has something to do with how margins are collapsed (combined).

from W3C Collapsing Margins:

In this specification, the expression collapsing margins means that adjoining margins (no non-empty content, padding or border areas or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin.

My theory is that since your first element is next to the body the two margins combine and are applied to the body: this forces the body's content to start below the applied collapsed margin in compliance with the box model.

There are situations where the margins do not collapse which might be worth playing around with (from Collapsing Margins):

* floated elements
* absolutely positioned elements
* inline-block elements
* elements with overflow set to anything other than visible (They do not collapse margins with their children.)
* cleared elements (They do not collapse their top margins with their parent block’s bottom margin.)
* the root element
digitaldreamer
  • 52,552
  • 5
  • 33
  • 28
19

This are some of the ways to avoid margin collapsing between parent-child elements. Use the one that fits better with the rest of your styling:

  • Set display to other than block.
  • Set float to other than none.
  • Remove the margin, and use instead padding. For example if you had margin-top: 10px, replace with padding-top: 10px;.
  • Remove the margin, and use instead position (absolute or relative) with attributes top, bottom, etc. For example if you had margin-top: 10px, replace with position: relative; top: 10px;.
  • Add a padding or a border in the side where the margins are collapsing, to the parent element. The border can be 1px and transparent.
  • Set overflow to other than visible to the parent element.
Jesús Carrera
  • 11,275
  • 4
  • 63
  • 55
5

display: flex will work in this case. Give parent element display: flex; and then give margin as per your requirement to the h1 tag.

PVitt
  • 11,500
  • 5
  • 51
  • 85
Bijay
  • 51
  • 1
  • 1
5

I know this is an old issue, I've come across it many times. The problem is that all of the fixes here are hacks that would potentially have unintended consequences.

First off, there's an easy explanation for the root problem. Due to the way that margin collapsing works, if the first element inside a container has a top margin, that top margin is effectively applied to the parent container itself. You can test this on your own by doing the following:

<div>
    <h1>Test</h1>
</div>

In a debugger, open this up and hover the div. You'll notice that the div itself actually is placed where the top-margin of the H1 element stops. This behavior is intended by the browser.

So there's an easy fix to this without having to resort to strange hacks, as most of the posts here do (no insults intended, its just the truth) - simply go back to the original explanation - ...if the first element inside a container has a top margin... - Following that, you'd therefore need the first element in a container to NOT have a top margin. Okay, but how do you do that without adding elements that don't interfere semantically with your document?

Easy! Pseudo-elements! You could do this via a class or a pre-defined mixin. Add a :before pseudo-element:

CSS via a class:

.top-margin-fix::before {   
    content: ' ';
    display: block;
    width: 100%;
    height: .0000001em;
}

With this, following the above markup example you would modify your div as such:

<div class="top-margin-fix">
    <h1>Test</h1>
</div>

Why does this work?

The first element in a container, if it has no top-margin, sets the position of the start of the next element's top margin. By adding a :before pseudo-element, the browser actually adds a non-semantic (in other words, good for SEO) element into the parent container before your first element.

Q. Why the height: .0000001em?

A. A height is required for the browser to push the margin element down. This height is effectively zero, but it will still allow you to add padding to the parent container itself. Since it's effectively zero, it won't have an effect on the layout of the container, either. Beautiful.

Now you can add a class (or better, in SASS/LESS, a mixin!) to fix this problem instead of adding weird display styles that will cause unexpected consequences when you want to do other things with your elements, purposefully eliminating margins on elements and/or replacing it with padding, or strange position/float styles that really aren't intended to resolve this issue.

schuelermine
  • 1,958
  • 2
  • 17
  • 31
dudewad
  • 13,215
  • 6
  • 37
  • 46
  • 2
    The .0000001em gets really rounded to zero in IE11, breaking your fix. When I change it to .01em it works though. – Esger Dec 05 '18 at 14:43
  • 1
    I imagine that .01em will be sufficient to still effectively be zero. Good point. On the upside, now that it's 2018, a lot of us will be dropping support for IE11 soon, especially with the recent announcement that MS will yet again be building a new browser (except this time, based on chromium, which means it - theoretically - won't totally suck). :D – dudewad Dec 06 '18 at 21:10
1

This happens because of margin collapse. When child element touches the boundary of parent element and any of them applied with margins then :

  1. The margin which is largest will win (applied).

  2. if any of them having margin then both will share the same.

Solutions

  1. apply border to parent which makes parent and child separates.
  2. apply padding to parent which makes parent and child separates.
  3. apply overflow rather than visible on parent.
  4. use before or after to create virtual element in parent div which can differ margin applied child and parent.
  5. create one html element between margin applied child and parent can also separates them .
Ishu
  • 117
  • 7
0

Run into this issue today.

overflow: hidden didn't worked as expected when I had more elements followed.

So I tried changing the parent div's display property and display: flex worked !!!

Hope this may help someone. :)

Libin
  • 2,442
  • 3
  • 20
  • 31
0

Adding a tiny bit of padding to the parent element top can fix this issue.

.parent{
  padding-top: 0.01em;
}

This is useful if you need an element inside the parent to be visible outside the parent element, like if you are creating an overlapping effect.