What is z-index
When we talk about 3D, we have the x,y,z dimension. z-index is the third dimension on a web page which determine how element stack. It works like Photoshop’s layer. In general, larger z-index stack on top of smaller z-index. But there still other factors that will affect the layer stacking, which are the position property in CSS and the html structure.
How z-index works
It is often to find that z-index doesn’t work as expected. Sometimes it work on Firefox but failed in IE 6. The first thing to make z-index work is you need to position that element. You can set the position property to absolute, fixed, or relative (but not static). Below is series of samples to test on how z-index stacking works.
Samples
<head></head>
<style>
div {width:100px; height:100px; position:absolute;}
#item_1 {background:#f00; margin-top:0px; margin-left:0px; z-index:5;}
#item_2 {background:#0f0; margin-top:50px; margin-left:20px; z-index:10;}
#item_3 {background:#00f; margin-top:100px; margin-left:40px; z-index:15;}
</style>
<body>
<div id=”item_1″>This is item1</div>
<div id=”item_2″>This is item2</div>
<div id=”item_3″>This is item3</div>
</body>
</html>
Sample 1-1 is a very simple case. Element #item_1, #item_2, and #item_3 are immediate child of document.body and has a distinct z-index with position set to absolute. The element with higher z-index stack on top of element with smaller z-index. Sweet!
<head></head>
<style>
div {width:500px; height:100px; position:absolute;}
span {display:block;position:absolute;}
.group{width:300px; height:400px;}
#group1{z-index:10;background:#900;margin-left:0px; margin-top:0px; border:1px solid #000;}
#group2{z-index:20;background:#090;margin-left:280px; margin-top:5px; border:1px solid #999}
#group1_item1 {background:#f00; margin-top:50px;z-index:40;}
#group1_item2 {background:#ff0; margin-top:100px;z-index:50;}
#group2_item1 {background:#0f0; margin-top:50px;z-index:30;}
#group2_item2 {background:#0ff; margin-top:100px;z-index:-1;}
</style>
<body>
<div id=”group1″ class=”group”>
<span>This is Group1 z-index = 10</span>
<div id=”group1_item1″>This is group1_item1 z-index = 40</div>
<div id=”group1_item2″>This is group1_item2 z-index = 50</div>
</div>
<div id=”group2″ class=”group”>
<span>This is Group2 z-index = 20</span>
<div id=”group2_item1″>This is group2_item1 z-index = 30</div>
<div id=”group2_item2″>This is group2_item2 z-index = -1</div>
</div>
</body>
</html>
Inside sample 1-2, I set the z-index of #group1 to 10 and z-index of #group2 to 20. Each group got 2 child element with different z-index value. Sample 1-2 might confuse some people that why the stacking order is not z-index = 50 stack on top of 40 then 30, 20, 10, and -1. The reason is HTML structure is taking into account when browser determine the display stacking.
Simply speaking:
- Child element will stack on top of parent element no matter what z-index value you got, even negative.
- When #group1 is stack behide #group2, all the child element in #group1 will stack behide #group2 (even one of #group2′s child has z-index = -1)
Till now, everything should work exactly the same in Firefox and IE 6. The sample below will show a IE 6 bug in z-index. Taking the previous sample and just remove the z-index setting for #group1.
<head></head>
<style>
div {width:500px; height:100px; position:absolute;}
span {display:block; position:absolute;}
.group{width:300px; height:400px;}
#group1{background:#900;margin-left:0px; margin-top:0px; border:1px solid #000;}
#group2{z-index:20;background:#090;margin-left:280px; margin-top:5px; border:1px solid #999}
#group1_item1 {background:#f00; margin-top:50px;z-index:40;}
#group1_item2 {background:#ff0; margin-top:100px;z-index:50;}
#group2_item1 {background:#0f0; margin-top:50px;z-index:30;}
#group2_item2 {background:#0ff; margin-top:100px;z-index:-1;}
</style>
<body>
<div id=”group1″ class=”group”>
<span>This is Group1 z-index = 10</span>
<div id=”group1_item1″>This is group1_item1 z-index = 40</div>
<div id=”group1_item2″>This is group1_item2 z-index = 50</div>
</div>
<div id=”group2″ class=”group”>
<span>This is Group2 z-index = 20</span>
<div id=”group2_item1″>This is group2_item1 z-index = 30</div>
<div id=”group2_item2″>This is group2_item2 z-index = -1</div>
</div>
</body>
</html>
When you open sample 1-2 and sample 1-3 in Firefox, you should found #group1_item1 and #group1_item2 is stack on top in sample 1-3 but not sample 1-2. While, if you open both sample in IE, there are no different. But which browser is correct in this? To explain this bug, here will need to introduce the concept on how z-index work.
The Concept
The way that browser determine which element stack on top is base on a tree like structure, i call it “z-index tree”. Sample 1-1 can be represented in the following “z-index tree”:

item3, with z-index:15, stack on top of item2, with z-index:10, and item2 stack on top of item1, with z-index:5. All three item stack on top of document body and other elements without position / z-index being set. Pretty simple.
For sample 1-2, the browser found that #group1, with z-index:10, should stack below #group2, with z-index:20. But #group1_item1, with z-index:30, will not stack on top of #group2. The reason is z-index stack with hierarchy.

As you can see in the fig. 1-2, the implication of #group1 stack below #group2 means all child nodes of #group1 will stack below #group2. If you know photoshop, z-index works somewhat like the layer folder.
For sample 1-3, Firefox and IE 6 will generate a different “z-index tree”. First take a look at figure 1-3-1, which is the “z-index tree” for Firefox.

In sample 1-3, #group1 does not have any z-index value, so it is in the same layer as document body. This is different from DOM tree, elements have no z-index and a correct position value will stack on the same layer as their parent element. So #group1 will not create a node there and affect its child nodes stacking. As #group1 is gone, #group1_item1 and #group1_item2 will be compared directly to #group2. This result in a stacking order with #group1_item2 on top then #group1_item1, then #group2.
But for IE 6, the “z-index tree” will look like figure 1-3-2.

When IE 6 encounter an element with position set to absolute / relative without a specified z-index, IE will set its z-index to zero. So #group1 become having z-index = 0 and stack below #group2. The resulting “z-index tree” of sample 1-3 will result in the same stacking as sample 1-2.
Some Tips
- Element stacking does not just relaying on the value of z-index, element position, html structure and browser will affect the way how element stack.
- When an element with higher z-index stacking below a smaller z-index element, there must be some parent element up stream having an z-index that’s too small.
- When dealing with IE 6, be very careful on the use of position property in CSS, and try to give it a z-index to make things clear.