Why isn't z-index working?
Practical answers:
Depending on what you want to achieve (visually) you either have to place the elements to be hidden inside a sibling of their current top level parent or you have to rely on visibility
to hide them.
Here is the parent sibling solution:
body{
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.first {
position: absolute;
z-index: 1;
width: 500px;
height: 500px;
background-color: grey;
animation: toggleOpacity 3s infinite;
}
.another-first {
z-index: 0;
}
.second {
position: relative;
z-index: 2;
width: 450px;
height: 450px;
top: 25px;
left: 25px;
background-color: orange;
opacity: 0.99;
}
.third {
position: absolute;
z-index: 3;
width: 400px;
height: 400px;
top: 25px;
left: 25px;
background-color: yellow;
opacity: 0.99;
}
.fourth {
position: absolute;
z-index: 20;
width: 350px;
height: 350px;
top: 25px;
left: 25px;
background-color: green;
opacity: 0.99;
}
.fifth {
position: absolute;
z-index: 5;
width: 300px;
height: 300px;
top: 25px;
left: 25px;
background-color: pink;
opacity: 0.99;
}
@-webkit-keyframes toggleOpacity {
0% { -webkit-transform: translateX(-150px); transform: translateX(-150px); }
50% { -webkit-transform: translateX(150px); transform: translateX(150px); }
100% {-webkit-transform: translateX(-150px);transform: translateX(-150px);}
}
@-moz-keyframes toggleOpacity {
0% { -moz-transform: translateX(-150px); transform: translateX(-150px); }
50% { -moz-transform: translateX(150px); transform: translateX(150px); }
100% {-moz-transform: translateX(-150px);transform: translateX(-150px);}
}
@-o-keyframes toggleOpacity {
0% { -o-transform: translateX(-150px); transform: translateX(-150px); }
50% { -o-transform: translateX(150px); transform: translateX(150px); }
100% {-o-transform: translateX(-150px);transform: translateX(-150px);}
}
@keyframes toggleOpacity {
0% { -webkit-transform: translateX(-150px); -moz-transform: translateX(-150px); -o-transform: translateX(-150px); transform: translateX(-150px); }
50% { -webkit-transform: translateX(150px); -moz-transform: translateX(150px); -o-transform: translateX(150px); transform: translateX(150px); }
100% {-webkit-transform: translateX(-150px);-moz-transform: translateX(-150px);-o-transform: translateX(-150px);transform: translateX(-150px);}
}
<div class="first"></div>
<div class="another-first">
<div class="second">
<div class="third">
<div class="fourth">
<div class="fifth">
</div>
</div>
</div>
</div>
</div>
Using Vals' solution and your original markup, it is possible to bring any of the divs in front by applying z-index:auto
to itself and a negative z-index
to its immediate child. The limitation here is that it can only be applied to one level. You can't completely reverse the stack with it (if we disable the reset line in JS
and click on level 2 and 4, level 4 comes above level 3 but not above level 2). Here's the snippet, click on any div:
window.ziToy = {
reset: false,
updateIndexes : function(){
$('div span').each(function(){
$(this).text($(this).parent().css('z-index'));
})
},
toggleReset : function () {
this.reset = !this.reset;
},
values:['-1','auto','1']
};
$('div').on('click', function(e){
e.stopPropagation();
if (window.ziToy.reset) {
$('div').css({'z-index':'auto'}); /*reset all divs*/
$(this).css({'z-index':'auto'});
$(this).children().css({'z-index':'-1'})
} else {
var toy = window.ziToy,
current = $(this).css('z-index'),
next = toy.values.indexOf(current) + 1;
$(this).css('z-index', toy.values[next % 3])
};
window.ziToy.updateIndexes();
});
window.ziToy.updateIndexes();
body {
color: white;
font-weight: bold;
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
box-sizing: border-box;
margin: 0;
padding: 0;
padding-top: 30px;
}
@media (max-height: 300px) {
body{
padding-top: 150px;
}
}
section {
width: 0;
height: 0;
overflow: visible;
left: -240px;
top: -160px;
position: relative;
z-index: 1;
}
.toggle {
position: absolute;
top:0;
left: 0;
padding: 15px;
color: #999;
font-weight: 400;
}
div {
position: absolute;
height: 150px;
width: 300px;
top: 30px;
left: 30px;
background-color: grey;
padding: 5px;
cursor: pointer;
}
div>div {
background-color: orange;
}
div>div>div {
background-color: darkred;
}
div>div>div>div {
background-color: green;
}
div>div>div>div>div {
background-color: pink;
}
div>span {float: right;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div><span></span>
<div><span></span>
<div><span></span>
<div><span></span>
<div><span></span>
</div>
</div>
</div>
</div>
</div>
</section>
<label class="toggle">
<input onchange="javascript: window.ziToy.toggleReset()" type="checkbox" />Reset all divs on click
</label>
Updated snippet: now you can disable the divs z-index reset to be able to toggle through values of -1
, auto
& 1
for each of the <div>
s independently. This will probably help in understanding the stacking contexts principle, laid down below in my own words.
Stacking contexts principle:
Each parent with a set position
(other than static) and a set z-index
(other than auto), creates a stacking context at that particular z-index
for all its children. Picture it as an infinity of z-index
for its children. That infinity is placed entirely at the z-index
of the parent.
Let's consider the reference item A
. Regardless of z-index
on any children of A
, if you set z-index
on B
(sibling of A
) higher than A
's z-index, B
(and any children of B
) will be rendered above A
and above all the children of A
.
When comparing z-index
of children from different parents, the browser will always decide based on z-index
of parents, not of children.
If you want to send a child below its parent, set z-index:auto
on the parent and a negative z-index
on the child.
Important note: When applying transforms (especially 3d) on elements with negative z-index
not all browsers behave the same and you might experience bugs and inconsistencies. For example, see this un-aswered question.
You can hack your way around to make child elements disappear behind the parent element with z-index
. Here are a few methods:
- Is it possible to have a child element behind his parent element with z-index
- How to get a parent element to appear above child
But generally speaking, you can't use z-index
to position a child element behind the root element of the stacking context.
If you can alter the HTML structure, however, to make the divs siblings, then you're all set:
.first {
z-index: 5;
width: 500px;
height: 500px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: grey;
}
.second {
z-index: 4;
width: 450px;
height: 450px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: orange;
}
.third {
z-index: 3;
width: 400px;
height: 400px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: yellow;
}
.fourth {
z-index: 2;
width: 350px;
height: 350px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: green;
}
.fifth {
z-index: 1;
width: 300px;
height: 300px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: pink;
}
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
<div class="fourth"></div>
<div class="fifth"></div>
The z-index
will be calculated relative to the parent, so once you increase the parent's z-index
all children will be affected implicitly. There is no way a child can be hidden behind its parent using z-index
. z-index
mostly affect siblings or HTML elements in different parents.