Why doesn't top: 0 work on absolutely-positioned elements relative to body?

 ̄綄美尐妖づ 提交于 2019-12-18 12:53:04

问题


You can see in the code below that the h1 pushes down the body and the absolutely-positioned block .absolute does not stick to the top. But you also can see that the same block is stuck to the top of its parent .wrapper. Why?

I'm not asking how to do that trick; I know how, e.g. padding instead margin to h1, or clearfix to parent and so on.

I'm interested in only one thing: why h1's margin pushes down the body, but is not pushing down .wrapper?

body {
  position: relative;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: silver;
}

.absolute {
  position: absolute;
  top: 0;
  left: 0;
  width: 50px;
  height: 50px;
  background-color: darkblue;
}

.wrapper {
  position: relative;
  overflow:hidden;
  width: 50%;
  height: 200px;
  overflow: hidden;
  background-color: yellow;
}

.wrapper > .absolute {
  background-color: darkcyan;
}
<div class="absolute"></div>
<h1>Some title</h1>

<div class="wrapper">
  <div class="absolute"></div>
  <h1>Some title</h1>
</div>

OK, I'll try to be more clear. If you click on the link below, you can see my JSFiddle. There is background-color: silver in the body tag. When I look in the code inspector, I see that the body tag begins slightly lower due to the h1 margin. But background-color begins at the top. This means that the code inspector is lying to me, and body begins from top. But why, then, does an absolutely-positioned element that's a direct child of body not start at the top?

JSFiddle


回答1:


As mentioned, the containing block of the top-level absolutely positioned element is body, as body is relatively positioned. When the margin of the h1 collapses with that of body, this causes the margin to affect body, and in turn the absolutely positioned element that it contains. Conversely, if body wasn't relatively positioned, the absolutely positioned element would be anchored to the initial containing block, and stick to the top of the viewport unaffected by any effective margins on body (because body is no longer its containing block).

As to why the silver background bleeds beyond the body element, that's by design:

3.11.1. The Canvas Background and the Root Element

The background of the root element becomes the background of the canvas and its background painting area extends to cover the entire canvas. However, any images are sized and positioned relative to the root element as if they were painted for that element alone. (In other words, the background positioning area is determined as for the root element.) The root element does not paint this background again, i.e., the used value of its background is transparent.

3.11.2. The Canvas Background and the HTML <body> Element

For documents whose root element is an HTML HTML element or an XHTML html element: if the computed value of ‘background-image’ on the root element is ‘none’ and its ‘background-color’ is ‘transparent’, user agents must instead propagate the computed values of the background properties from that element's first HTML BODY or XHTML body child element. The used values of that BODY element's background properties are their initial values, and the propagated values are treated as if they were specified on the root element. It is recommended that authors of HTML documents specify the canvas background for the BODY element rather than the HTML element.

The root element's default background color is always transparent, so setting a background color on the html element prevents the silver background from bleeding out of the body element (and you'll see that the inspector is in fact not lying to you):

html {
  background-color: white;
}

body {
  position: relative;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: silver;
}

.absolute {
  position: absolute;
  top: 0;
  left: 0;
  width: 50px;
  height: 50px;
  background-color: darkblue;
}

.wrapper {
  position: relative;
  overflow:hidden;
  width: 50%;
  height: 200px;
  overflow: hidden;
  background-color: yellow;
}

.wrapper > .absolute {
  background-color: darkcyan;
}
<div class="absolute"></div>
<h1>Some title</h1>

<div class="wrapper">
  <div class="absolute"></div>
  <h1>Some title</h1>
</div>



回答2:


It's because collapsing margins. How to disable it, you could read in some articles/answers, for example, this.
You are using overflow: hidden for .wrapper, and it disables collapsing margins for this block. From the specification:

Margins of a box with ‘overflow’ other than ‘visible’ do not collapse with its children's margins.

But it seems like overflow: hidden doesn't work for <body>, because if I set
height: 0/max-height: 0 it doesn't work too:

body {
    height: 0;
    max-height: 0;
}
<div>Some test text</div>

I think it's because (from the specification):

UAs must apply the 'overflow' property set on the root element to the viewport. When the root element is an HTML "HTML" element or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, user agents must instead apply the 'overflow' property from the first such child element to the viewport, if the value on the root element is 'visible'.

That's why margins between <body> and first <h1> collapse (from MDN):

Parent and first/last child
If there is no border, padding, inline content, or clearance to separate the margin-top of a block with 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 with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.

That's why margins between <html> and <body> don't collapse (from the specification):

Margins of the root element's box do not collapse.

I also noticed that if <html> has default background-color and <body> has specified background-color + margin, the background color fills the entire space of <html>, for example:

html, body {
    height: 100%;
}

body {
    margin: 20px;
    background-color: yellow;
}

But if you set background-color to <html>, it works like a normal block, for example:

html, body {
    height: 100%;
}

html {
    background-color: red;
}

body {
    margin: 20px;
    background-color: yellow;
}

I summary, I recommend you to use other approaches to disable collapsing margins, or add another wrapper for <body>, for example:

body {
    margin: 0;
    padding: 0;
}

.body-wrapper {
    position: relative;
    overflow: hidden;
    background-color: silver;
}

.absolute {
    position: absolute;
    top: 0;
    left: 0;
    width: 50px;
    height: 50px;
    background-color: darkblue;
}

.wrapper {
    position: relative;
    width: 50%;
    height: 200px;
    overflow: hidden;
    background-color: yellow;
}

.wrapper > .absolute {
    background-color: darkcyan;
}
<div class="body-wrapper">
    <div class="absolute"></div>
    <h1>Some title</h1>

    <div class="wrapper">
        <div class="absolute"></div>
        <h1>Some title</h1>
    </div>
</div>



回答3:


This is another instance of the phenomenon known as margin-collapsing.

I'll try to explain what is happening:

When you move div.absolute with position absolute, it pulls it out of the flow of the content. This causes the first h1 to act as the first-child of its parent, which is body in this case.

The h1's margin then gets collapsed outside the body, due to margin-collapsing. That is why top: 0; is not in the upper-left of the viewport. It is in the upper left of the body.

If you need to get this to work, then add a html {position: relative; overflow: hidden:}.




回答4:


This is interesting behavior with little documentation to be found online (at least on a basic search).

I'll start by saying I don't know the reason why the absolutely positioned box doesn't align to the corner of body, as it would be expected to do. But here are a few observations:

  1. The expected positioning works on IE11. The gap exists in Chrome and FF.

  2. If you remove position: relative from body, the expected positioning works (as tested in Chrome, FF & IE11). demo

  3. If you apply just 1px padding to body, it also works cross-browser. demo

  4. If you add a border to body, it also works. demo


UPDATE

As a quick addendum to @BoltClock's accepted answer, this problem could have been easily illustrated and understood by just adding a background color to the root element in the original code...

html { background-color: red; }

demo

... and by removing the default margin on the h1:

h1 { margin: 0; }

demo




回答5:


This is caused by h1 having a default margin, which is keeping the body apart from h1 and its sibling .absolute.

So all you have to do is reset the margin in h1, like you did for body:

snippet with every margin resetted

body {
  position: relative;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: silver;
}
h1 {
  margin: 0;
 /*if you want to see full text, put this into the flow*/   
  position:relative;
  z-index:1;
}
.absolute {
  position: absolute;
  top: 0;
  left: 0;
  width: 50px;
  height: 50px;
  background-color: darkblue;
}
.wrapper {
  position: relative;
  overflow: hidden;
  width: 50%;
  height: 200px;
  overflow: hidden;
  background-color: yellow;
}
.wrapper > .absolute {
  background-color: darkcyan;
}
<div class="absolute"></div>
<h1>Some title</h1>

<div class="wrapper">
  <div class="absolute"></div>
  <h1>Some title</h1>
</div>

snippet with every no margin resetted

body {
  position: relative;
  /* margin: 0; */
  padding: 0;
  overflow: hidden;
  background-color: silver;
}
h1 {
  /* margin: 0; */
  /*if you want to see full text, put this into the flow*/
  position: relative;
  z-index: 1;
}
.absolute {
  position: absolute;
  top: 0;
  left: 0;
  width: 50px;
  height: 50px;
  background-color: darkblue;
}
.wrapper {
  position: relative;
  overflow: hidden;
  width: 50%;
  height: 200px;
  overflow: hidden;
  background-color: yellow;
}
.wrapper > .absolute {
  background-color: darkcyan;
}
<div class="absolute"></div>
<h1>Some title</h1>

<div class="wrapper">
  <div class="absolute"></div>
  <h1>Some title</h1>
</div>


来源:https://stackoverflow.com/questions/33594788/why-doesnt-top-0-work-on-absolutely-positioned-elements-relative-to-body

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!