问题
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 XHTMLhtml
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 HTMLBODY
or XHTMLbody
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 theBODY
element rather than theHTML
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 setheight: 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 themargin-top
of a block with themargin-top
of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate themargin-bottom
of a block with themargin-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:
The expected positioning works on IE11. The gap exists in Chrome and FF.
If you remove
position: relative
frombody
, the expected positioning works (as tested in Chrome, FF & IE11). demoIf you apply just 1px
padding
tobody
, it also works cross-browser. demoIf 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