When/How does z-index break stacking context? [duplicate] - css

This question already has an answer here:
Understanding z-index stacking order
(1 answer)
Closed 6 years ago.
My understanding is that z-index elements are only stacked with their siblings. I've also read that z-index "doesn't work" with position: fixed. However, I'm using the two together and it happened to do exactly what I wanted it to; and I want to understand why.
I have a blocking div that needs to cover everything in the page except one element. The element is not a sibling of the blocker, which I thought would make it impossible, but it worked. What rule is being exploited here, and how can I predict if it will work in other browsers?
.top {
background-color: yellow;
}
.yes {
position: relative;
z-index: 10;
background-color: blue;
}
.no {
position: relative;
z-index: 1;
background-color: red;
}
.blocker {
position: fixed;
z-index: 5;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .7);
}
<div class="top">
<button class="yes">
Yes
</button>
<button class="no">
No
</button>
</div>
<div class="blocker">
</div>

Every non-positioned box belongs to the same stacking context that's established by the closest ancestor that meets any of the criteria listed here, or the root element if there is no such ancestor. This means that elements don't have to be siblings of one another in order to participate in the same stacking context. They may share a distant ancestor (in HTML, every element descends from the root element), but they don't have to share a parent.
.top does not establish a new stacking context for its descendants .yes and .no as it is non-positioned and its z-index is auto. Therefore, .yes, .no and .blocker all participate in the same stacking context — the root stacking context. As a result the mere fact that .yes has a higher z-index than .blocker causes it to be painted in front.
Note that while each of .yes, .no and .blocker does establish its own stacking context, the stacking contexts they establish are not pertinent here; it's the stacking context that they participate in, and they all participate in the same root stacking context.

Related

Can someone explain stacking contexts?

I recently had a bug where a video player wasn't clickable inside a jquery ui dialog.
I ended up fixing the problem by overriding the position:relative; with a position:inherit;
Other solutions to the problem included removing the position:relative; entirely or by making the z-index of the player class be something other than 1.
As I've read, these are both indicative of changing the stacking context in this situation which solved my problem. However, I still don't really understand what was going on in my situation or stacking contexts in general. Does anyone else have any good examples/suggesstions as to what may have been going on?
<div class="player">
<div id="videoPlayer_wrapper" style=" position: relative; width:580px; height: 192px;">
<object type="application/x-shockwave-flash" data="/flash/player.swf" width="100%" height="100%" bgcolor="#000000" id="videoPlayer" name="videoPlayer" tabindex="0">
</object>
</div>
</div>
where the CSS for player is
.player {
margin-bottom: 20px;
position: relative;
z-index: 1;
}
I found the article you linked by Phillip Walton to be most helpful to me understanding stacking contexts... I went on this study safari in the course of debugging an issue of my own.
Note that the pink square with z-index: 100; appears below the light blue square with z-index: 1; because it's constrained by a stacking context created on .A by the transform.
This jsbin is a bit easier to experiment with than the SO inline code: https://jsbin.com/lataga/2/edit?css,output
div {
width: 200px;
height: 200px;
padding: 1rem;
}
.A {
position: absolute;
background-color: red;
/*
Adding a transform here creates a
new stacking context for the children of .A
*/
transform: translateX(0);
/*
several other properties can trigger creation of stacking context,
including opacity < 1
*/
/* opacity: 0.99; */
/*
If we raise .A above .B, the children will rise up with it;
uncomment the following to see:
*/
/* z-index: 3; */
}
.a {
position: relative;
/*
even a much higher z-index can't lift .a above .b when
it is constrained to a lower stacking context
*/
z-index: 100;
margin-left: 125px;
background-color: pink;
}
.B {
position: absolute;
margin-top: 75px;
/* z-index: 2; */
background-color: blue;
}
.b {
margin-left: 50px;
background-color: lightblue;
/*
The following is not necessary: if a z-index is not specified,
then it is calculated according to the rules of
natural stacking order...
I'm just specifying it explicitly for editorial effect.
*/
z-index: 1;
}
<div class="A">
A: 1
<div class="a">a: 1.100</div>
</div>
<div class="B">
B: 2
<div class="b">b: 2.1</div>
</div>
Stacking Context
Positioning and assigning a z-index value to an HTML element creates a stacking context, (as does assigning non-full opacity).
Stacking contexts can be contained in other stacking contexts, and together create a hierarchy of stacking contexts.
Each stacking context is completely independent from its siblings: only descendant elements are considered when stacking is processed.
Each stacking context is self-contained: after the element's contents are stacked, the whole element is considered in the stacking order of the parent stacking context.
Note: The hierarchy of stacking contexts is a subset of the hierarchy of HTML elements, because only certain elements create stacking contexts. We can say that elements that do not create their own stacking contexts are assimilated by the parent stacking context.
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
Put differently
Every stacking context has a single HTML element as its root element. When a new stacking context is formed on an element, that stacking context confines all of its child elements to a particular place in the stacking order. That means that 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!
...
[...] In addition to opacity, several newer CSS properties also create stacking contexts. These include: transforms, filters, css-regions, paged media, and possibly others. As a general rule, it seems that if a CSS property requires rendering in an offscreen context, it must create a new stacking context.
http://philipwalton.com/articles/what-no-one-told-you-about-z-index/

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;

Pseudo element on parent hidden behind child image on IE8

Why in IE8, is the background color of a pesudo element flowing behind children of the parent? The text flows in front, but the background-color does not. Z-index did not seem to help any.
I haven't been able to determine if this is a bug in IE8 or not. It seems like this would have been a pretty common use-case, but I couldn't find many blog posts or SO questions related to it.
http://jsfiddle.net/VAg2E/
<div id="parent">
<img src="http://placehold.it/200x200">
</div>
#parent{ padding: 20px; }
#parent:before{
content: 'Behind the image';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: red;
}
Edit : A related Stack Overflow Question about Stacking Order
This is definitely a bug in IE8; since your :before pseudo-element is positioned, it should create a new stacking context and always be drawn on top of the img unless you give it a negative z-index (even then, the entire element should be drawn behind it, not just its background).
This issue also seems specific to stacking between :before and :after pseudo-elements and replaced elements like img. It looks like IE8 is treating replaced content differently in terms of stacking, but whatever it is doing, it's definitely not conforming to the spec.
As you're probably aware, this is fixed in IE9.
Have your exact same issue, the only thing you can do is force the stacking order via CSS and z-index. The only catch is that z-index is placed on child element starting from parent element, so you wont be able to do a proper logic order as #parent-element {z-index: 2} and #child-element {z-index: 1}, the z-index for the #child-element will just be set to level 1 as a separate stack order inside the #parent-element.
You can still set z-index for the #child-element with a -1 value, it will just get back the whole #parent-element stacking order.
So to recap:
#parent-element { z-index: 99;} /* or any arbitrary number fitting */
#child-element {z-index: -1;}
Also remember to give both elements a position: relative/absolute to enable the stacking order fo z-index
IE8 only supports pseudos if <!DOCTYPE> is declared. Source
#parent { padding: 20px; z-index: 2; }
#parent:before {
content: 'Behind the image';
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: red;
z-index: -1;
}​

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

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.

Is it possible to set the stacking order of pseudo-elements below their parent element? [duplicate]

This question already has answers here:
How to get a child element to show behind (lower z-index) than its parent? [duplicate]
(7 answers)
Why can't an element with a z-index value cover its child?
(5 answers)
Closed 3 years ago.
I am trying to style a element with the :after pseudo element CSS selector
#element {
position: relative;
z-index: 1;
}
#element::after {
position:relative;
z-index: 0;
content: " ";
position: absolute;
width: 100px;
height: 100px;
}
It seems like the ::after element can not be lower then the element itself.
Is there a way to have the pseudo element lower then the element itself?
Pseudo-elements are treated as descendants of their associated element. To position a pseudo-element below its parent, you have to create a new stacking context to change the default stacking order.
Positioning the pseudo-element (absolute) and assigning a z-index value other than “auto” creates the new stacking context.
#element {
position: relative; /* optional */
width: 100px;
height: 100px;
background-color: blue;
}
#element::after {
content: "";
width: 150px;
height: 150px;
background-color: red;
/* create a new stacking context */
position: absolute;
z-index: -1; /* to be below the parent element */
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Position a pseudo-element below its parent</title>
</head>
<body>
<div id="element">
</div>
</body>
</html>
I know this is an old thread, but I feel the need to post the proper answer. The actual answer to this question is that you need to create a new stacking context on the parent of the element with the pseudo element (and you actually have to give it a z-index, not just a position).
Like this:
#parent {
position: relative;
z-index: 1;
}
#pseudo-parent {
position: absolute;
/* no z-index allowed */
}
#pseudo-parent:after {
position: absolute;
top:0;
z-index: -1;
}
#parent { position: relative; z-index: 1; }
#pseudo-parent { position: absolute; } /* no z-index required */
#pseudo-parent:after { position: absolute; z-index: -1; }
/* Example styling to illustrate */
#pseudo-parent { background: #d1d1d1; }
#pseudo-parent:after { margin-left: -3px; content: "M" }
<div id="parent">
<div id="pseudo-parent">
</div>
</div>
Try it out
el {
transform-style: preserve-3d;
}
el:after {
transform: translateZ(-1px);
}
There are two issues are at play here:
The CSS 2.1 specification states that "The :beforeand :after pseudo-elements elements interact with other boxes, such as run-in boxes, as if they were real elements inserted just inside their associated element." Given the way z-indexes are implemented in most browsers, it's pretty difficult (read, I don't know of a way) to move content lower than the z-index of their parent element in the DOM that works in all browsers.
Number 1 above does not necessarily mean it's impossible, but the second impediment to it is actually worse: Ultimately it's a matter of browser support. Firefox didn't support positioning of generated content at all until FF3.6. Who knows about browsers like IE. So even if you can find a hack to make it work in one browser, it's very likely it will only work in that browser.
The only thing I can think of that's going to work across browsers is to use javascript to insert the element rather than CSS. I know that's not a great solution, but the :before and :after pseudo-selectors just really don't look like they're gonna cut it here.
Speaking with regard to the spec (http://www.w3.org/TR/CSS2/zindex.html), since a.someSelector is positioned it creates a new stacking context that its children can't break out of. Leave a.someSelector unpositioned and then child a.someSelector:after may be positioned in the same context as a.someSelector.
I know this question is ancient and has an accepted answer, but I found a better solution to the problem. I am posting it here so I don't create a duplicate question, and the solution is still available to others.
Switch the order of the elements. Use the :before pseudo-element for the content that should be underneath, and adjust margins to compensate. The margin cleanup can be messy, but the desired z-index will be preserved.
I've tested this with IE8 and FF3.6 successfully.
Set the z-index of the :before or :after pseudo element to -1 and give it a position that honors the z-index property (absolute, relative, or fixed). This works because the pseudo element's z-index is relative to its parent element, rather than <html>, which is the default for other elements. Which makes sense because they are child elements of <html>.
The problem I was having (that lead me to this question and the accepted answer above) was that I was trying to use a :after pseudo element to get fancy with a background to an element with z-index of 15, and even when set with a z-index of 14, it was still being rendered on top of its parent. This is because, in that stacking context, it's parent has a z-index of 0.
Hopefully that helps clarify a little what's going on.
I fixed it very simple:
.parent {
position: relative;
z-index: 1;
}
.child {
position: absolute;
z-index: -1;
}
What this does is stack the parent at z-index: 1, which gives the child room to 'end up' at z-index: 0 since other dom elements 'exist' on z-index: 0. If we don't give the parent an z-index of 1 the child will end up below the other dom elements and thus will not be visible.
This also works for pseudo elements like :after
I don't know if someone will have the same issue with this. The selected answer is partially correct.
What you need to have is:
parent{
z-index: 1;
}
child{
position:relative;
backgr

Resources