While working on a task, where I had to have a fixed positioned toolbar at the top of a scrollable panel, I stumbled on this quirky(?) behaviour of the CSS property "opacity".
This simplistic fiddle is self explainatory, but to summarize: If you have two DIVs, one is fixed positioned and the other one scrolls behind(?) the fixed one, then the translucent content (opacity < 1) in the scrollable div shows up over fixed div. The opacity of the fixed div is of no consequence here.
While the problem goes away if I assign the fixed div a z-index greater than the scrollable div (see this fiddle), I am still curious to know why the opacity property behaves in a counter intuitive manner in this case.
Can some one please explain?
By the way, the behaviour is consistent across Chrome 35, FF 30 and IE 10.
Copying the code from jsfiddle here - as it's the norm.
CSS
.fixedOpaqueDiv {
position: fixed;
background-color: #ABABAB;
width: 100%;
height: auto;
text-align: center;
font-size: 20px;
color: #DDDDDD;
padding-top: 5px;
opacity: 1;
}
.scrollingDiv {
padding-top: 140px;
text-align: center;
color: blue;
font-weight: bold;
}
.opaque {
opacity: 1;
}
.translucent {
opacity: 0.5;
}
HTML
<div style="height: 300px; overflow: auto;">
<div class="fixedOpaqueDiv">I am a div with fixed position,
<BR>with same z-index as the div below.
<BR>I am supposed to be Opaque, but
<BR>apparently I am opaque ONLY to the opaque divs below,
<BR>and not to the translucent ones</div>
<div class="scrollingDiv">
<div class="translucent" style="color: red; padding: 5px;">^^^^^^^^ Scroll up ^^^^^^^^</div>
<div class="opaque">Opaque 1</div>
<div class="translucent">Translucent 1</div>
<div class="opaque">Opaque 2</div>
<div class="translucent">Translucent 2</div>
<div class="opaque">Opaque 3</div>
<div class="translucent">Translucent 3</div>
<div class="opaque">Opaque 4</div>
<div class="translucent">Translucent 4</div>
<div class="opaque">Opaque 5</div>
<div class="translucent">Translucent 5</div>
<div class="opaque">Opaque 6</div>
<div class="translucent">Translucent 6</div>
<div class="opaque">Opaque 7</div>
<div class="translucent">Translucent 7</div>
<div class="opaque">Opaque 8</div>
<div class="translucent">Translucent 8</div>
</div>
</div>
This has to do with the fact that an element with opacity
less than 1 creates a stacking context. Elements that don't establish stacking contexts will be obscured by your fixed-position element as expected, but the translucent elements that do establish stacking contexts will be painted in front because they appear after the fixed-position element in the source.
The reason setting z-index
works is because you then make the stack level of the fixed-position element greater than that of the scrollable element's contents.
Section 9.9 of the CSS2.1 spec summarizes the exact order in which elements are stacked. The last two items in the list are relevant to this scenario:
Within each stacking context, the following layers are painted in back-to-front order:
- 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 non-positioned floats.
- 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 stack level 0.
- the child stacking contexts with positive stack levels (least positive first).
In #6, "child stacking contexts" refer to the translucent elements, and the positioned descendant is the fixed element. Once you set z-index
on the fixed element, it falls under #7, which ensures it will appear over the translucent content.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With