Can I position an inner element below its parent? [duplicate] - css

This question already has answers here:
Why can't an element with a z-index value cover its child?
(5 answers)
Closed 1 year ago.
It seems that with markup like http://jsfiddle.net/nwekR/
<div id="container">
Outer Div
<div id="inner">Inner Div</div>
</div>
and CSS like
#container {
position: relative;
z-index: 6;
}
#inner {
position: absolute;
z-index: 4;
}
#inner is still above #container can I have #inner below?

No.
The CSS 2.1 spec states this standard of painting elements:
the background and borders of the
element forming the stacking
context.
the child stacking contexts with
negative stack levels (most negative
first).
the in-flow, non-inline-level,
non-positioned descendants.
the floating descendants.
the in-flow, inline-level,
non-positioned descendants,
including inline tables and inline
blocks.
the child stacking contexts with
stack level 0, and the positioned
descendants with 'z-index: auto'.
the child stacking contexts with
positive stack levels (least
positive first).
Given these rules and your HTML, #container is creating the stacking context for the element #inner, which means #container has to be rendered first.
Other people have already posted alternative HTML/CSS to get the effect you desired, but if you want to know more about why what you want isn't possible, here is the documentation:
http://www.w3.org/TR/CSS21/visuren.html#layers

You just need to put it outside of inner element when it comes to HTML.
Here's your solution: http://jsfiddle.net/nwekR/23/
<div id="container">
Outer Div
</div>
<div id="inner">Inner Div</div>

#container {
background: yellow;
position: relative;
height: 100px;
-moz-box-shadow: 0 2px 6px rgba(0,0,0,0.6);
-webkit-box-shadow: 0 2px 6px rgba(0,0,0,0.6);
box-shadow: 0 2px 6px rgba(0,0,0,0.6);
z-index: 6;
}
#inner {
background: orange;
position: absolute;
z-index: 4;
width: 100px;
height: 50px;
top: 180px;
right: 0;
padding-top: 20px;
}

The cleanest solution is to add an extra wrapper element, and to move #inner outside #container.
The extra wrapper element is given position: relative, so everything else should be the same as it was before, with the exception that #inner is underneath #container.
Live Demo

It is not possible(in it's current state of having inner a child of container) because when the browser renders the DOM, it goes from top down on the DOM tree, and there is no way to draw under something that has already been drawn(i.e. drawing the parent before child).
You can read more about the z-index here.
There are ways of accomplishing(by changing the html around) this however, you can see thirtydot's solution.

Related

Absolute positioned child is displaced by static parent [duplicate]

This question already has answers here:
Why aren't my absolutely/fixed-positioned elements located where I expect?
(3 answers)
Is there any difference between 'margin-top, -left, -bottom, -right' and 'top, left, bottom, right' properties when element is positioned absolutely?
(2 answers)
CSS margin terror; Margin adds space outside parent element [duplicate]
(7 answers)
Closed 3 years ago.
I have a div child with absolute position (blue-box) and a parent with default (static) position hanging on body (container). When I define a margin-top for the parent, div child is displaced too.
Blue-box is positioned relative to its closest positioned ancestor, so in this case is positioned relative to body, because container is positioned statically. (See https://developer.mozilla.org/en-US/docs/Web/CSS/position#absolute)
I supposed that was a problem of margin collapsing, but I read that the margins of floating and absolutely positioned elements never collapse.
(See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing)
body {
background-color: #1f1f1f;
height: 2000px;
}
.box {
width: 100px;
height: 100px;
margin-bottom: 10px;
}
.blue-box {
background: lightskyblue;
position: absolute;
margin-top: 110px;
}
.green-box {
background: lightgreen;
}
.container {
background: rgba(0, 0, 0, 0.4);
height: 200px;
width: 200px;
margin-top: 150px;
}
<div class="container">
<div class="box blue-box"></div>
</div>
<div class="box green-box"></div>
I expected blue-box to be on top of the viewport and not inside the container box
As the second sentence in the Mozilla docs say,
"The top, right, bottom, and left properties determine the final location of positioned elements.", you have to specify one of these values, to actually position it.
.blue-box{position: absolute;top:10px}
Check this codepen : https://codepen.io/jsuryahyd/pen/ExYGbxr
By the way, this got me thinking too. Thank you.

Why is a textnode rendered below its parents` ::before by default?

On writing-up an answer for a different question on SO, I made this snippet:
#import url('https://fonts.googleapis.com/css?family=Shadows+Into+Light');
/* relevant CSS */
div {
position: relative;
}
div::before {
content: '';
position: absolute;
top: 0; left:0;
}
div>span {
position:relative;
z-index:0;
}
/* rest is just styling - should be irrelevant for the question */
div {
font: normal normal normal 2rem/1 'Shadows Into Light', cursive;
color: white;
text-align:center;
margin: 1rem;
padding: 1rem;
min-width: 15rem;
cursor: pointer;
}
div::before {
width: 100%;
height: 100%;
opacity: 1;
transition: opacity .3s cubic-bezier(.4,0,.2,1);
background-color: #bada55;
border-radius: .4rem;
}
div[orange]:before {
background-color: #f50;
}
div:hover::before {
opacity:.65;
}
body {
margin: 0; padding: 0;
}
center-me-please {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background: transparent url(http://lorempixel.com/g/1200/800/fashion) no-repeat 50% 50% /cover;
}
<center-me-please>
<div><span>#bada55</span></div>
<div orange>not so #bada55</div>
I was surprised to notice the ::before element is rendered above textnodes (orange element) and, in order to prevent it from happening. I had to wrap the textnode in a span element and give it a non-negative z-index and a non-static position (#bada55 element).
On inspection, while the ::before element has a default (expected) value of z-index (auto), the textnode doesn't seem to have one at all (or at least Chrome's not able to show it).
Up to now I liked to think of myself as a z-index little ninja, idea partly backed-up by developing this toy to help friends and colleagues better understand stacking contexts principle and z-index in general.
As you might have guessed, I'm looking for any explanation on why ::before is not rendered below everything else in an element by default (it's first, therefore below, right?) and for any evidence about this being either a (known?) bug or intended (by design?) behavior.
A spec I might have missed or misinterpreted would be great.
::before is painted below text content by default — and the default case is when everything is non-positioned.
But your ::before is absolutely positioned. Positioned boxes are always painted in front of non-positioned boxes. Refer to section 9.9.1 (emphases mine):
Within each stacking context, the following layers are painted in back-to-front order:
the background and borders of the element forming the stacking context.
the child stacking contexts with negative stack levels (most negative first).
the in-flow, non-inline-level, non-positioned descendants.
the non-positioned floats.
the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
the child stacking contexts with positive stack levels (least positive first).
Wrapping your text content in a positioned span causes it to be painted in front of the ::before content as expected since then you have two positioned boxes in source order.

Relative positioning without z-index causes overlapping

I created two sibling divs and applied negative margin on the last one, but when I added position: relative to the first one, it overlapped its next sibling:
.box {
width: 100px;
height: 100px;
}
.box-1 {
position: relative;
background: orange;
}
.box-2 {
margin-top: -50px;
background: yellowgreen;
}
<div class="box box-1">box-1</div>
<div class="box box-2">box-2</div>
However, MDN says that
positioned (absolutely or relatively) with a z-index value other than "auto" will create a new stacking context.
So I guess it isn't the stacking context that causes overlapping, any idea about how this happens?
Standard blocks in the normal flow, without any positioning property, are always rendered before positioned elements, and appear below them, even if they come later in the HTML hierarchy.
Example being
.absolute {
position: absolute;
background:purple;
left: 80px;
}
.relative {
position: relative;
left:50px;
background:yellow;
top:-50px;
}
div {
width:100px;
height:100px;
border: 1px dashed #999966;
background:blue;
}
<div class="absolute">absolute</div>
<div>normal</div>
<div class="relative">relative</div>
<div>normal</div>
Something cool about relative though, is that it's still considered to be in it's original location, even if it's been moved if you're using left, right, top, bottom. If you use margins to position the element the boundaries of the container are also moved with it. This can be seen using the same example above but changing the relative position to use margining. Reference to relative positioning
Non-positioned elements are always rendered before explicitly positioned elements. This means that by applying position: relative to 'box-1', it is rendered after 'box-2' and so appears on top of it.
The overlapping is caused by margin-top: -50px; in your CSS
Here is a decent explanation:
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/Stacking_without_z-index
Standard blocks in the normal flow, without any positioning property, are always rendered before positioned elements, and appear below them, even if they come later in the HTML hierarchy.

Override CSS Z-Index Stacking Context

I'm trying to override / ignore the stacking context for an element so it can be positioned on the z-axis relative to the page root.
However, according to the article What No One Told You About Z-Index:
If an element is contained in a stacking context at the bottom of the stacking order, there is no way to get it to appear in front of another element in a different stacking context that is higher in the stacking order, even with a z-index of a billion!
New stacking contexts can be formed on an element in one of three ways:
When an element is the root element of a document (the element)
When an element has a position value other than static and a z-index value other than auto
When an element has an opacity value less than 1
With the following example:
.red, .green, .blue { position: absolute; }
.red { background: red; }
.green { background: green; }
.blue { background: blue; }
<div><span class="red">Red</span></div>
<div><span class="green">Green</span></div>
<div><span class="blue">Blue</span></div>
If the first div is given opacity:.99;, (which creates a new stacking context on the first node) then even if .red has z-index:1, it will still be placed behind the other elements because it is just rendered as the highest element within that stack.
Working Demo in jsFiddle
Which looks like this:
Q: Is there a way for an element to ignore the stack context of any of it's parent elements and ask to be positioned relative to the original stack context of the page?
Q: Is there a way for an element to ignore the stack context of any of it's parent elements and ask to be positioned relative to the original stack context of the page?
No, it's not possible to transfer a positioned element between stacking contexts without repositioning the element in the DOM. You cannot even move an element to the root stacking context by using position: fixed or position: absolute (as you have observed, .red is being positioned relative to its parent, div:first-child because it creates a new stacking context).
That being said, given your HTML and CSS it should be trivial to just reassign the classes to the div elements instead, as shown in other answers and here so all your divs and spans participate in the root stacking context:
<div class="red"><span>Red</span></div>
<div class="green"><span>Green</span></div>
<div class="blue"><span>Blue</span></div>
But your situation probably isn't as simple as it seems.
We can do it using 3D transformation and we will be able to bring any element to the front even if it's trapped inside a stacking context:
.red,
.green,
.blue {
position: absolute;
width: 100px;
color: white;
line-height: 100px;
text-align: center;
}
body,
div:first-child {
transform-style: preserve-3d; /* this is important for the trick to work */
}
.red {
top: 20px;
left: 20px;
background: red;
/*z-index: 1; we no more need this */
transform:translateZ(1px); /* this will do the trick */
}
.green {
top: 60px;
left: 60px;
background: green;
}
.blue {
top: 100px;
left: 100px;
background: blue;
}
<div><span class="red">Red</span></div>
<div><span class="green">Green</span></div>
<div><span class="blue">Blue</span></div>
More details and examples here: Why can't an element with a z-index value cover its child?
As it stated in the The stacking context: "Using z-index, the rendering order of certain elements is influenced by their z-index value. This occurs because these elements have special properties which cause them to form a stacking context.
To partly overcome stacking content problem you can use css properties to display unwanted elements:
opacity: 0.1;
or
display: none;

Absolute positioned child div expands to fit the parent?

Is there anyway for an absolute positioned child to expand to fill its relative positioned parent? (The height of parent is not fixed)
Here is what i did and it is working fine with Firefox and IE7 but not IE6. :(
<div id="parent">
<div id="child1"></div>
</div>
#parent { position: relative; width: 200px; height:100%; background:red }
#child1 { position: absolute; top: 0; left: 200px; height: 100%; background:blue }
That's easy. The trick is setting top: 0px and bottom: 0px at the same time
Here's the working code
html, body {
width: 100%;
height: 100%;
overflow: hidden;
}
#parent {
display: block;
background-color: #ff0;
border: 1px solid #f00;
position: relative;
width: 200px;
height: 100%;
}
#child1 {
background-color: #f00;
display: block;
border: 1px solid #ff0;
position: absolute;
left: 200px;
top: 0px;
bottom: 0px;
}
Check out a working example here http://jsfiddle.net/Qexhh/
If I remember correctly there is a bug with how IE6 handles div height. It will only create the div to the height needed to contain the content within it when height is set to 100%. I would recommend two approaches:
Don't worry about supporting IE6 as it is a dead browser anyway
If that doesn't work, use something like jQuery to get the height of the parent div and then set the child div to that height.
fake it by setting the backgrounds to be the same colour so no-one notices the difference
You can achieve this with setting both the top and bottom attributes of the child.
See how this is done
At the bottom of that article, there is a link to Dean Edwards' IE7 (and IE8) js library that you should include for IE6 visitors. It is a JS library that actually MAKES IE6 behave like IE7 (or 8) when you include it. Sweet!
Dean Edwars' IE7 and 8 JS libraries
As far as I know, there is no way of expanding a parent element around an absolutely positioned child element. By making the child element absolutely positioned your are removing it from the regular flow of page items.
I recently built a 2-column website where the right column was absolutely positioned but the left column was not. If the left column had less content and a smaller height than the right column, the page would cut off the right column since it was absolutely positioned.
In order to resolve this, I had to determine if the height of the right column was greater than the height of the left column and if so set the height of the parent div height to the greater of the two.
Here is my jQuery solution. I'm not much of a coder so feel free to tweak this:
jQuery(function(){
var rightColHeight = jQuery('div.right_column').height();
var leftColHeight = jQuery('div.left_column').height();
if (rightColHeight > leftColHeight){
jQuery('.content_wrap').height(rightColHeight+'px');
}
});

Resources