Search code examples
htmlcsspositionz-index

z-index and stacking; removing a child from stacking context


I'm trying to tweak the stacking order of some elements; here's an SSCCE.

Given this HTML snippet:

<div id="block1">
    <div>Lorem ipsum dolor.</div>
    <div id="widget">
        <div>Widgety goodness!</div>
    </div>
</div>
<div id="block2">
    <div>Lorem ipsum dolor.</div>
</div>
<div id="block3">
    <div>Lorem ipsum dolor.</div>
</div>

I'm trying to display #widget on top of #block2; which with the following CSS, you'll see is problematic without changing the location of #widget in the node tree.

#block1, #block2, #block3 {
    position: relative;
    min-height: 50px;
    width: 100%;
}
#block1, #block3 {
    background-color: #abc;
    color: #123;
    z-index: 1;
}
#block2 {
    background-color: #def;
    color: #456;
    z-index: 2;
}
#widget {
    background-color: #f00;
    position: absolute;
    z-index: 999;
    left: 200px;
    top: 40px;
}

As you can see in the fiddle, #widget is overlapped partially by #block2; which makes perfect sense as the parent #block1 is lower among siblings in the stacking order.

The obvious answer is: make #widget a child of #block2, but unfortunately that's not a solution here. Additionally, the #blockN elements cannot have their relative z-index values modified; blocks 1 and 3 must be lower than 2. (by this I mean the calculated values could be different, but #block2 will always be greater than it's siblings). The reason for this, is box-shadow layering.

Is there any reasonable (read: cross-browser, IE7+) way to remove #widget from it's stacking context, while retaining it's position, coordinate location, dimensions, etc.?

Using fixed positioning removes it as desired, however it also obtains all the visual properties of being fixed (which makes it a non-solution)

I reckon this may have been answered in a roundabout way, however I didn't find it. Also, apologies; it appears I may have missed some innocuous but key details in my initial post. I think I've covered them all now.


Solution

  • Your rules aren't really applying to the elements that you want them to be applied to. You need to explicitly set the z-index on the elements you want to position, in your case that element is the div child of block2. By simply modifying the first selector from:

    #block1, #block2 , #block3 {
    

    to

    #block1, #block2 div, #block3 {
    

    Your stacking order is corrected. Your z-index rules were being applied to the parents while the children continued to use the default of auto. In your case, by setting the position on #block2 div, you allow your widget to sit on top of the block2 child div.

    jsFiddle example