注册

我愿赌上一包辣条,这些定位相关你不知道

写码写累了不如来看看这些奇奇怪怪的css,腰不酸腿不疼,一阵轻松加愉快(还能偷我表情包)

子元素的绝对定位原点在哪?

用了那么久的定位,有注意过子元素的绝对定位原点,是在盒子模型的哪一处,在 padding、border 还是 content?

a7ee3817f5c4636beca2afdf398f6cd1.png

实践出真知。既然不确定那就实操个例子看看。

<div class="father">
father
<div class="child">child</div>
</div>
body {
background-color: rgb(20, 19, 19);
}

.father {
width: 300px;
height: 300px;
margin: 40px;
border: 20px solid rgb(202, 30, 30);
padding: 40px;
position: relative;
background-color: #eee;
}

.child {
width: 50px;
height: 50px;
position: absolute;
top: 0;
left: 0;
background-color: rgb(228, 207, 17);
}

在 Chrome 90 的版本下的表现。

13a34dd270f64f9a326cbb1c11cee108.png


更换 Edge、火狐、IE 浏览器,以及设置 box-sizing 分别为 border-boxcontent-box,最后的结果都表现一致。


从结果上看,绝对定位的字元素是紧贴着父元素的内边框,绝对定位的原点就是在父元素 padding 的左上角。


如果绝对定位的父亲们都没有设置 relative,那么是将会是定位在哪?

body 下只有一个绝对定位的元素,设置了 bottom:0,那么他的表现将会是如何呢?定位在 body 的底边?

<body>
I am body
<div class="absolute">I am absoluted</div>
</body>
html {
background-color: #fff;
}

body {
height: 50vh;
background-color: #ddd;
}
.absolute {
width: 120px;
height: 50px;
position: absolute;
bottom: 0;
left: 0;
background-color: rgb(0, 0, 0);
color: #fff;
}

341fbdabe0789ddeeb721a72c12cc4a5.png

从结果上看,绝对定位的元素并不是相对于 body 进行定位的,也不是根据 html 标签,此时的 html 的宽高等同于 body 的宽高,而是根据浏览器视口进行定位的。


所有父元素position 的属性是static 的时候,绝对定位的元素会被包含在初始包含块中,初始包含块有着和浏览器视口一样的大小,所以从表现上来看,就是绝对定位的元素是根据浏览器视口定位。


如果把top和left去掉,那么位置依旧是他原来文档流的位置,只是不占空间了,后面的元素会窜上来。

通过 HTML 结构控制层叠上下文

在使用定位属性时,必不可少的使用 z-index 属性,使用 z-index 属性会创建一个层叠上下文。z-index 值不会在整个文档中进行比较,而只会在该层叠上下文中进行比较。

<div class="bar"></div>
<div class="container">
<div class="bottom-box-1">
<div class="top-box"></div>
</div>
</div>
<div class="container">
<div class="box-container">
<div class="bottom-box-2"></div>
<div class="top-box"></div>
</div>
</div>
body {
display: flex;
justify-content: center;
align-items: center;
height: 90vh;
}
.bar {
position: absolute;
top: 65vh;
z-index: 2;
width: 100vw;
height: 20px;
background: #8bbe6e;
}

.top-box {
position: absolute;
z-index: 3;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50%;
height: 50%;
background: #626078;
filter: brightness(60%);
}

.bottom-box-1 {
position: absolute;
top: 45%;
z-index: 1;
transition: top 1s;
width: 40px;
height: 40px;
background: #626078;
}

.container:hover .bottom-box-1 {
top: 72%;
}

.box-container {
position: absolute;
top: 45%;
transition: top 1s;
}

.bottom-box-2 {
position: relative;
z-index: 1;
width: 40px;
height: 40px;
background: #626078;
}

.container {
border: 2px dashed #626078;
height: 80%;
width: 100px;
margin: 20px;
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}

.container:hover .box-container {
top: 72%;
}

e497c573c8e95d93c30e07e5feeadc5c.png

鼠标分别移入两个虚线框内,我们发现,第二个例子 bar 穿过了两个正方形。


这两者的区别就在于 HTML 结构,在第一个例子中,小正方形在大正方形的里面,大正方形在移动的时候,小正方形也随之移动,但是因为大正方形对决定位且有 z-index 属性不为 auto,因此创建了一个层叠上下文,这导致大正方形内的所有元素都是在这个层叠上下文里层叠。


那么第二个例子是怎么解决的呢?


第二个例子的技巧在于引入了一个新的 div 来包裹这两个正方形,这个新的 div 只负责移动。而里面的大小正方形和 bar 处于同一个层叠上下文中。这样子就可以产生 bar 从两个正方形中穿过的效果。


还没懂的来看图来看图:

1c9db14f9c1a73f70c32bc01fe1b4128.png

总结一下创建层叠上下文的几种情况(别怪我枯燥,就是这么多):



  • 文档根元素<html>;
  • position 值为 relative(相对定位)或 absolute(绝对定位)且 z-index 值不为 auto 的元素;
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素;
  • flex (flexbox) 容器的子元素,且 z-index 值不为 auto;
  • grid 容器的子元素,且 z-index 值不为 auto;
  • opacity 属性值小于 1 的元素;
  • mix-blend-mode 属性值不为 normal 的元素;
  • 以下任意属性值不为 none 的元素:

    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border


  • isolation 属性值为 isolate 的元素;
  • -webkit-overflow-scrolling 属性值为 touch 的元素;
  • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素
  • contain 属性值为 layoutpaint 或包含它们其中之一的合成值(比如 contain: strictcontain: content)的元素;

好了,你学废了嘛~


当定位遇到 Transform

transform 下 absolute 宽度被限制

以前,我们设置 absolute 元素宽度 100%, 则都会参照第一个非static值的position祖先元素计算,没有就window. 现在,=-=,需要把transform也考虑在内了。


默认情况下我们设置 absolute 的宽度 100%,会根据第一个不是static的祖先元素计算,没有就找视口宽度。现在也考虑 CSS3 的 transform 属性了。


<div class="relative">
<div class="transform">
<div class="absolute">i am in transform</div>
</div>
</div>
<div class="relative">
<div class="no-transform">
<div class="absolute">i am not in transform</div>
</div>
</div>
.relative {
position: relative;
width: 400px;
height: 100px;
background-color: rgb(233, 233, 233);
}

.transform {
transform: rotate(0);
width: 200px;
}

.no-transform {
width: 200px;
}

.absolute {
position: absolute;
width: 100%;
height: 100px;
background-color: rgb(137, 174, 255);
}

564b9649f03c50169f4f9695199bf543.png

可以看到绝对定位的宽度是相对 transform 的大小计算了。

transform 对 fixed 的限制

身为大聪明的你,加了一行position:fixed安心上线,结果预览机一瞅,没生效??

position:fixed 正常情况下可以让元素不跟随滚动条滚动,这种行为也无法通过 relative/absolute 来限制。但是遇到 transform,他就被打败了,降级成 absolute
<div class="demo">
<div class="box">
<div class="fixed">
<p>没有transform</p>
<img src="https://gitee.com/zukunft/MDImage/raw/master/img/20210516172759.jpg" alt="">
</div>
</div>

<div class="relative box">
<div class="fixed">
<p> 有relative</p>
<img src="https://gitee.com/zukunft/MDImage/raw/master/img/20210516172759.jpg" alt="">
</div>
</div>
<div class="transform box">
<div class="fixed">
<p>有transform</p>
<img src="https://gitee.com/zukunft/MDImage/raw/master/img/20210516172759.jpg" alt="">
</div>
</div>
</div>
.box {
height: 250px;
}

.demo {
height: 9999px;
}


.fixed {
position: fixed;
}

.relative {
position: relative;
}

.transform {
transform: scale(1);
}

a9fbe4626fee8173c8efa63c52e48fdb.png

诶,神奇不,滚起来了,就只有被transform包裹的元素会被滚走。
根据W3C的定义,transform属性值会使元素成为一个包含块,它的后代包括absolute元素,fixed元素受限在其 padding box 区域。所以滚动的时候,transform元素被滚走,其子元素也跟随tranform滚走。


关于包含块

上面提到了包含块,那到底如何形成的包含块,包含块又是个啥子

在 MDN 中的解释

The size and position of an element are often impacted by its containing block. Percentage values that are applied to the width, height, padding, margin, and offset properties of an absolutely positioned element (i.e., which has its positionset to absolute or fixed) are computed from the element's containing block.
即一个元素的尺寸和位置受到它的**包含块(containing block)**的影响。对于一些属性,例如 width, height, padding, margin,绝对定位元素的偏移值 (比如 position 被设置为 absolutefixed),当我们设置百分比值的时候,它的这些值计算,就是通过该元素的包含块的值来计算的。

通常情况下,包含块就是这个元素最近的祖先块元素的内容区域,但实际可能不是;


我们可以通过 position 的属性来确定它的包含块;



  1. 如果 position 属性为 staticrelativesticky,包含块可能由它的最近的祖先块元素(比如说 inline-block, blocklist-item 元素)的内容区的边缘组成,也可能会建立格式化上下文(比如说 table container,flex container, grid container, 或者是 block container 自身)
  2. 如果 position 属性为 **absolute** ,包含块就是由它的最近的 position 的值不是 static (也就是值为fixed, absolute, relativesticky)的祖先元素的内边距区的边缘组成。
  3. 如果 position 属性是 fixed,在连续媒体的情况下(continuous media)包含块是 viewport ,在分页媒体(paged media)下的情况下包含块是分页区域(page area)。
  4. 如果 position 属性是 absolutefixed,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的:

    1. transformperspective的值不是 none
    2. will-change 的值是 transformperspective
    3. filter的值不是 nonewill-change 的值是 filter(只在 Firefox 下生效).
    4. contain 的值是 paint (例如: contain: paint;)



需要注意的是根元素(<html>)所在的包含块是一个被称为初始包含块的矩形。他的尺寸是视口 viewport (for continuous media) 或分页媒体 page media (for paged media).


如果所有的父元素都没有显式地定义position属性,那么所有的父元素默认情况下 position 属性都是static。结果,绝对定位元素会被包含在初始包含块中;这个初始块容器有着和浏览器视口一样的尺寸,并且<html>元素也被包含在这个容器里面。简单来说,绝对定位元素会被放在<html>元素的外面,并且根据浏览器视口来定位。

总结

遇到奇奇怪怪的css问题不要慌~硬调不是好办法不如来我这瞅瞅~~ 万一就解决了呢✿✿ヽ(°▽°)ノ✿


链接:https://juejin.cn/post/6963235293587750943

0 个评论

要回复文章请先登录注册