Why does “position: relative” interfere with “tran

2019-07-09 08:28发布

Given the following markup and style

div {
  width: 300px;
  height: 50px;
  border: 1px solid black;
  display: inline-block;
  transition: all .1s ease-in-out;
  background-color: white;
  padding: 0px 5px;
}
div:hover {
  transform: scale(1.2);
}
label {
  /*position: relative;*/
}
<div>
  <label>some random text</label>
</div>
<div>
  <label>some random text</label>
</div>

when hovering over the first div some letter from the second div are being "hidden" under the scaled element. When, however, position: relative is set on the label element, the text gets rendered over the scaled element:

div {
  width: 300px;
  height: 50px;
  border: 1px solid black;
  display: inline-block;
  transition: all .1s ease-in-out;
  background-color: white;
  padding: 0px 5px;
}
div:hover {
  transform: scale(1.2);
}
label {
  position: relative;
}
<div>
  <label>some random text</label>
</div>
<div>
  <label>some random text</label>
</div>

I'm trying to understand the reasoning behind this. Since this seams consistent across browsers, I'm thinking that it is defined in the specs. If so, what's the rationale? And how do I "turn it off", if can't touch the relative positioning?

1条回答
Ridiculous、
2楼-- · 2019-07-09 08:44

Applying a transform to an element causes it to establish a new stacking context.

Positioning an element (i.e. setting its position to something other than static) doesn't necessarily cause it to establish a stacking context, in particular a relatively positioned element with z-index: auto (the default) does not establish a stacking context.

That being said, both types of elements are grouped together in the painting order defined in section 9.9 of CSS2.1:

Within each stacking context, the following layers are painted in back-to-front order:

  1. the background and borders of the element forming the stacking context.
  2. the child stacking contexts with negative stack levels (most negative first).
  3. the in-flow, non-inline-level, non-positioned descendants.
  4. the non-positioned floats.
  5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
  7. the child stacking contexts with positive stack levels (least positive first).

When you hover the first div, it becomes a child stacking context with stack level 0, but this child stacking context participates in the same parent stacking context as the label in the second div as the second div itself doesn't establish a stacking context.

As all of your elements have the same stack level of 0 (based on the default z-index: auto), the spec says:

Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.

Since your first div occurs before the second div and its label, the label of the second div is painted over the first div despite the transform.

You can fix this by specifying z-index: 1 on div:hover:

div {
  width: 300px;
  height: 50px;
  border: 1px solid black;
  display: inline-block;
  transition: all .1s ease-in-out;
  background-color: white;
  padding: 0px 5px;
}
div:hover {
  transform: scale(1.2);
  z-index: 1;
}
label {
  position: relative;
}
<div>
  <label>some random text</label>
</div>
<div>
  <label>some random text</label>
</div>

查看更多
登录 后发表回答