当绝对定位遇上层叠上下文

mattuy 2020年05月18日 291次浏览

css中position: absolute可以将元素指定为绝对定位,那么绝对定位元素的几何位置是相对于谁计算呢?

MDN上的描述是:

绝对定位元素相对于最近的非 static 祖先元素定位。当这样的祖先元素不存在时,则相对于ICB(inital container block, 初始包含块)。

也即是说,绝对定位元素应该相对其position为非static的最近祖先定位,或者这样的祖先不存在时相对初始包含快(一般为body)定位。而position默认为static。

但似乎有时候也有例外。

今天想在一个scroller组件中使用绝对定位突破到更顶层,但意外的发现,即使从该元素的父元素到body都没有指定position属性,它竟然仍是相对scroller容器定位而不是body或者更上层的具有position: relative属性的元素。

实验了半天发现是因为scroller使用了css的transform属性来变换滚动元素的位置以提升性能。而这直接导致了具有有效transform属性的元素成了绝对定位元素的基准。

考虑如下html:

<div style="line-height: 50px;">
    <style>
        .relative-container {
            background: lightcoral;
            position: relative;
            height: 300px;
        }
        .midware {
            background: lightcyan;
            height: 200px;
        }
        .absolute-block {
            background: black;
            height: 30px;
            position: absolute;
            top: 0;
            width: 30px;
        }
    </style>
    <div class="relative-container">
        ------把下面的div撑下去一点方便观察差异-------
        <div class="midware">
            <div class="absolute-block"></div>
        </div>
    </div>
</div>
------把下面的div撑下去一点-------

如上,absolute-block成功突破了midwaretop: 0是相对于relative-container而不是其直接父元素midware。但是当我们为midware添加transform属性后:

.midware {
    background: lightcyan;
    height: 200px;
    transform: translateZ(0);
}
------把下面的div撑下去一点-------

absolute-block已经相对其父元素midware定位了!但midwareposition并没有被显示指定。transform设置为其他有效值效果也一样。

为了测试transform属性是否隐式改变了position属性,我添加了如下代码:

const el = document.querySelector('.midware');
console.log(getComputedStyle(el).position); // static

并没有。这个现象应该是由其他机制引起的。

最后查了下资料,有人说是由层叠上下文导致的。但又不是所有的层叠上下文都会造成这样的影响,不同内核的浏览器也有不同的表现。所以应该将这个特性当作bug来对待?

参考:
不受控制的 position:fixed