写码写累了不如来看看这些奇奇怪怪的css,腰不酸腿不疼,一阵轻松加愉快(还能偷我表情包)
子元素的绝对定位原点在哪?
用了那么久的定位,有注意过子元素的绝对定位原点,是在盒子模型的哪一处,在 padding、border 还是 content?
实践出真知。既然不确定那就实操个例子看看。
<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 的版本下的表现。
更换 Edge、火狐、IE 浏览器,以及设置 box-sizing
分别为 border-box
和 content-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;
}
复制代码
从结果上看,绝对定位的元素并不是相对于 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%;
}
复制代码
鼠标分别移入两个虚线框内,我们发现,第二个例子 bar 穿过了两个正方形。
这两者的区别就在于 HTML 结构,在第一个例子中,小正方形在大正方形的里面,大正方形在移动的时候,小正方形也随之移动,但是因为大正方形对决定位且有 z-index 属性不为 auto
,因此创建了一个层叠上下文,这导致大正方形内的所有元素都是在这个层叠上下文里层叠。
那么第二个例子是怎么解决的呢?
第二个例子的技巧在于引入了一个新的 div 来包裹这两个正方形,这个新的 div 只负责移动。而里面的大小正方形和 bar 处于同一个层叠上下文中。这样子就可以产生 bar 从两个正方形中穿过的效果。
还没懂的来看图来看图:
总结一下创建层叠上下文的几种情况(别怪我枯燥,就是这么多):
- 文档根元素
<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
属性值为layout
、paint
或包含它们其中之一的合成值(比如 contain: strict
、contain: 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);
}
复制代码
可以看到绝对定位的宽度是相对 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);
}
复制代码
诶,神奇不,滚起来了,就只有被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 itsposition
set toabsolute
orfixed
) are computed from the element’s containing block.
即一个元素的尺寸和位置受到它的**包含块(containing block)**的影响。对于一些属性,例如 width
, height
, padding
, margin
,绝对定位元素的偏移值 (比如 position
被设置为 absolute
或 fixed
),当我们设置百分比值的时候,它的这些值计算,就是通过该元素的包含块的值来计算的。
通常情况下,包含块就是这个元素最近的祖先块元素的内容区域,但实际可能不是;
我们可以通过 position
的属性来确定它的包含块;
- 如果
position
属性为static
、relative
或sticky
,包含块可能由它的最近的祖先块元素(比如说inline-block
,block
或list-item
元素)的内容区的边缘组成,也可能会建立格式化上下文(比如说table container
,flex container
,grid container
, 或者是block container
自身) - 如果
position
属性为**absolute**
,包含块就是由它的最近的position
的值不是static
(也就是值为fixed
,absolute
,relative
或sticky
)的祖先元素的内边距区的边缘组成。 - 如果
position
属性是fixed
,在连续媒体的情况下(continuous media)包含块是viewport
,在分页媒体(paged media)下的情况下包含块是分页区域(page area)。 - 如果
position
属性是absolute
或fixed
,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的:transform
或perspective
的值不是none
will-change
的值是transform
或perspective
filter
的值不是none
或will-change
的值是filter
(只在 Firefox 下生效).contain
的值是paint
(例如:contain: paint;
)
需要注意的是根元素(<html>
)所在的包含块是一个被称为初始包含块的矩形。他的尺寸是视口 viewport (for continuous media) 或分页媒体 page media (for paged media).
如果所有的父元素都没有显式地定义position
属性,那么所有的父元素默认情况下 position
属性都是static
。结果,绝对定位元素会被包含在初始包含块中;这个初始块容器有着和浏览器视口一样的尺寸,并且<html>
元素也被包含在这个容器里面。简单来说,绝对定位元素会被放在<html>
元素的外面,并且根据浏览器视口来定位。
总结
遇到奇奇怪怪的css问题不要慌~硬调不是好办法不如来我这瞅瞅~~ 万一就解决了呢✿✿ヽ(°▽°)ノ✿
走过路过不要错过,求个关注求个赞~