CSS layout mystery
Among the many two (or three) column layout techniques I sometimes use the following one:
<div class="left1">
<div class="left2">
left main content
</div>
</div>
<div class="right1">
<div c开发者_开发百科lass="right2">
right sidebar
</div>
</div>
together with:
.variant1 .left1 {
float: left;
margin-right: -200px;
width: 100%;
}
.variant1 .left1 .left2 {
margin-right: 200px;
}
.variant1 .right1 {
float: right;
width: 200px;
}
This works in all major browsers. But for some very strange reason exactly the same technique but reversed doesn't work:
.variant2 .left1 {
float: left;
width: 200px;
}
.variant2 .right1 {
float: right;
margin-left: -200px;
width: 100%;
}
.variant2 .right1 .right2 {
margin-left: 200px;
}
In the second variant all text in the sidebar cannot be selected and all links cannot be clicked. This is at least true for Firefox and Chrome. In IE7 the links can at least be clicked and Opera seems completely fine.
Does anyone know the reason for this strange behaviour? Is it a browser bug?
Please note: I am not looking for a working two column CSS layout technique, I know there are loads of them. And I don't necessarily need this technique to work. I only like to understand the reason why the second variant behaves like it does.
Here is a link to a small test page which should illustrate the problem: http://selfthinker.org/stuff/css_layout_mystery.html
It's a basic layering issue.Let's parse the CSS spec on visual formatting to find out why.
The order in which the rendering tree is painted onto the canvas is described in terms of stacking contexts.
Okay, let's see what "stacking context" we're in. For that, we'll need to know when a new stacking context is created.
[A z-index value of an]...integer is the stack level of the generated box in the current stacking context. The box also establishes a local stacking context in which its stack level is '0'.
Well, we don't have any z-index values - so they're all auto.
The root element forms the root stacking context. Other stacking contexts are generated by any positioned element (including relatively positioned elements) having a computed value of 'z-index' other than 'auto'.
Nope, no positioned elements either. Looks like we're all in the "root stacking context".
Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.
That certainly explains why .right1
paints over .left1
- it's after it in source order. (Note that you'd see the paint-over issue better if you removed the margin-left: 200px
from .right2
).
So, now that we know the problem (and that it's according to spec) - how do we fix it? Easiest thing to do would be just make the z-index of .left1
to be higher than .right1
. Since they're in the same stacking context, the higher z-index will override the source order:
.variant2 .left1 { position: relative; z-index: 1; }
Or, if we keep reading the spec - we'll notice that:
Each stacking context consists of the following stacking levels (from back to front):
- the background and borders of the element forming the stacking context.
- the stacking contexts of descendants with negative stack levels.
- stacking level containing in-flow non-inline-level non-positioned descendants.
- stacking level for non-positioned floats and their contents.
- a stacking level for in-flow inline-level non-positioned descendants.
- a stacking level for positioned descendants with 'z-index: auto', and any descendant stacking contexts with 'z-index: 0'.
- the stacking contexts of descendants with positive stack levels.
which means we can actually just do:
.variant2 .left1 { position: relative; }
which will give .left1
a "stacking level" of 6 - which will override .right1
's stacking level of 4.
精彩评论