包含块与position
相信大家对于绝对定位的定义都背得滚瓜烂熟了:相对于第一个非static
的上层元素定位,但你是否想过CSS
中的包含块(containing block)也会对其产生影响。
我们先来看几个定义。
position: absolute
的MDN
定义:
相对定位的元素并未脱离文档流,而绝对定位的元素则脱离了文档流。在布置文档流中其他元素时,绝对定位元素不占据空间。绝对定位元素相对于_最近的非 static 祖先元素_定位。当这样的祖先元素不存在时,则相对于 ICB(initial containing block,初始包含块)。
初始包含块的MDN
定义:
根元素(html)所在的包含块是一个被称为初始包含块的矩形。它具有视口(对于连续媒体)或页面区域(对于分页媒体)的尺寸。
包含块的MDN
定义:
一个元素的尺寸和位置经常受其包含块(containing block)的影响。大多数情况下,包含块就是这个元素最近的祖先块元素的内容区域,但也不是总是这样。
MDN
:如何确定包含块
确定一个元素的包含块的过程完全依赖于这个元素的 position 属性:
- 如果 position 属性为 static、relative 或 sticky,包含块可能由它的最近的祖先块元素(比如说 inline-block, block 或 list-item 元素)的内容区的边缘组成,也可能会建立格式化上下文 (比如说 a table container, flex container, grid container, 或者是 the 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 的值是 layout、paint、strict 或 content(例如:contain: paint;)
- backdrop-filter 的值不是 none(例如:backdrop-filter: blur(10px);)
从MDN
对绝对定位的定义中,如果一个绝对定位的DOM
没有一个父元素有非static
的position
,那么他的定位是根据初始包含块,也就是<html>
所在的矩形区域。但是在MDN
对如何确定包含块的第4点中,貌似用transform
等也能创造包含块,那就来试试。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
.container {
width: 400px;
height: 400px;
background-color: green;
position: relative;
}
.parent {
width: 200px;
height: 200px;
background-color: black;
margin-left: 50px;
/* transform: translateX(2px); */
/* contain: paint; */
/* will-change: transform; */
/* filter: blur(5px); */
/* backdrop-filter: blur(10px); */
}
.target {
width: 100px;
height: 100px;
background-color: skyblue;
position: absolute;
left: 50px;
}
</style>
<body>
<div class="container">
<div class="trans">
<div class="parent"></div>
</div>
</div>
</body>
</html>
目前的target是相对于.container
定位的,也就是第一个非static
的父元素。
我们给.parent
加上transform
属性试一下:
.parent {
/* ... */
transform: translateX(2px);
}
可以看到这时.target
是相对于.parent
定位,也就是相对于他的包含块去定位的,我们再试一下其他几个属性
.parent {
/* 挨个试一遍 */
contain: paint;
/* will-change: transform; */
/* filter: blur(5px); */
/* backdrop-filter: blur(10px); */
}
结果都是绝对定位的元素相对于包含块定位,包括MDN
文档上说的只在Firefox
生效的filter
属性在Chrome
上也是作为绝对定位的元素的包含块的,所以我们在使用绝对定位的时候,应注意这些属性对绝对定位的影响。
包含块不但会影响元素的绝对定位,还会影响元素的尺寸及位,如 width、height、padding、margin,所以在绝对定位的元素中,他的width
等属性也是从上述规则确定的包含块中来的,上面的.target
的width
如果为60%
,那么他参考的元素就是包含块.parent
还有我们常用的position: fixed
,MDN
定义如下:
元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform、perspective、filter 或 backdrop-filter 属性非 none 时,容器由视口改为该祖先。
当有上层元素满足包含块的定义时,fixed
元素也会根据他来定位。
而当position
的值为static
、relative
或 sticky
,元素相对于最近的上层块元素去定位。
怎么做
- 如果一个
DOM
使用的是absolute
或fixed
定位,应在他的上层元素中注意transform
等属性的使用,避免定位的参考点不是自己想要的。 - 或者如果想要一个
DOM
摆脱文档流的控制,可以用JavaScript
计算出准确的坐标后,在body
中插入此元素再定位,同时可以监听其他DOM
的位置变化让目标DOM
跟着改变。
欢迎点赞、收藏、转发~ 你的一个小小的赞是我最大的鼓励~
原文链接:https://juejin.cn/post/7343243882027237391 作者:plutoLam