33

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:

demo

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?

Community
  • 1
  • 1
KyleMit
  • 30,350
  • 66
  • 462
  • 664

3 Answers3

24

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.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 15
    If he's asking this kind of question, it probably isn't as simple as his provided example and would be a pain to change; but as you pointed out, his only choice would be to reassign the classes or move the elements. – Nahydrin Nov 08 '13 at 04:54
  • BoltClock, thanks for your answer, although @BrianGraham is right about this example being used to illustrate my problem, rather than a problem in and of itself. The actual issue I was working on was to answer another question on SO: [How do I get Bootstrap Tour to work with a jQuery dialog?](http://stackoverflow.com/a/19846898/1366033), which I ultimately solved through rearranging the DOM. – KyleMit Nov 08 '13 at 12:52
  • I am dealing with this now but its not as easy as reordering the dom since its component I imported with react, I guess i could man-handle it to make it work but its always a bit tricky once you start reorganizing someone elses component that is in essence its own library. So is there anything newer that would handle this? i know its an old question. – carinlynchin Oct 09 '18 at 19:00
  • @carinlynchin: Nope, not that I'm aware of. – BoltClock Oct 10 '18 at 04:28
11

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?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    Great answer! The z-index debacle is one of the worst parts of the current web standards. There is no way you can reliably position one element above another unless you have control over where they live in the stacking tree. Any parent element can create a stacking context through CSS that cannot be repaired in CSS by its children. The only way to reliably position one element above another with z-index is if you can guarantee they live in the same stacking context. Which often, you cannot. This answer allows us to bypass that altogether by relying on transform instead. – Stijn de Witt Jun 02 '22 at 18:16
  • 1
    If this does not work for you, there might be a parent element with transform property applied. – Sebastian Jung Dec 22 '22 at 14:00
  • It worked but having doubts to apply this to the `body` element because I'm not sure what all CSS, perspective changes it'll do to the whole app. – Ritesh Jagga Jun 12 '23 at 13:42
1

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;
Roman
  • 19,236
  • 15
  • 93
  • 97