你不知道的CSS之包含块

什么是包含块?

包含块(containing block),顾名思义,就是一个包含其他元素的“块”。但它可不只是一个简单的容器哦!它不仅影响子元素的位置,还会影响到**宽度、长度、内外边距(padding、margin)、甚至定位(position)**等各种CSS属性的计算。

例如,像下图一样,很多时候我们看不见它的存在,但它正在幕后默默工作。它的作用,就是决定元素的尺寸和位置,特别是当我们使用百分比值时,计算依据就是包含块的尺寸

https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-142459.png

是不是感觉它像个隐藏的大boss,一切似乎都在它的掌控之中?


简单的例子,告诉你包含块怎么“作妖”!

好,让我们从一个简单的例子开始:

1
2
3
4
5
6
7

<body>
<div class="container">
<div class="item"></div>
</div>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13

.container {
width: 500px;
height: 300px;
background-color: skyblue;
}

.item {
width: 50%;
height: 50%;
background-color: red;
}

根据上面的代码,你一定会说:div.item的宽度是250px,高度是150px。嗯,没错!但是你知道吗,这个答案虽然正确,但并不完全准确!真正的计算方式是这样的:

div.item的尺寸,实际上是通过它的包含块来计算的!

在这个例子里,div.item的包含块是div.container,也就是它的父元素。因为div.container的宽度是500px,所以50%的宽度就是250px;高度300px,所以50%就是150px。

所以,记住:百分比计算的依据是包含块的尺寸,而不是父元素的尺寸!


包含块的规则:绝对定位的奇妙之处

好,现在我们进入了包含块的“黑洞”——对于绝对定位元素,包含块是怎样计算的呢?看一下这个神秘的例子:

1
2
3
4
5
6
7
8

<body>
<div class="container">
<div class="item">
<div class="item2"></div>
</div>
</div>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

.container {
width: 500px;
height: 300px;
background-color: skyblue;
position: relative;
}

.item {
width: 300px;
height: 150px;
border: 5px solid;
margin-left: 100px;
}

.item2 {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 10px;
top: 10px;
}

你可能已经脑补了div.item2的位置,但让我来帮你补充一下:div.item2的包含块是div.container,而不是div.item!因为div.containerposition: relative;的属性,它是一个定位容器。

所以当你使用position: absolute;时,包含块的计算会参照最近的定位祖先元素,而不是简单的父元素。这就是为什么div.item2的包含块是div.container的原因,而不是div.item


一些更“迷”的例子:Transform与包含块

接下来,让我们来看看transform如何影响包含块的计算:

1
2
3
4
5
6
7
8

<body>
<div class="container">
<div class="item">
<div class="item2"></div>
</div>
</div>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

.container {
width: 500px;
height: 300px;
background-color: skyblue;
position: relative;
}

.item {
width: 300px;
height: 150px;
border: 5px solid;
margin-left: 100px;
transform: rotate(0deg); /* 新增代码 */
}

.item2 {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
left: 10px;
top: 10px;
}

这里,我们为.item添加了transform: rotate(0deg),看似没变化,但在实际渲染中,你会发现**div.item2的包含块变成了div.item**,而不再是div.container。原因就是:transform让div.item变成了一个块容器,因此div.item2的包含块就“变脸”了。


惊天大揭秘!包含块的实际应用

让我们总结一下吧!你已经掌握了最重要的几点:

  • 根元素html)的包含块就是浏览器的视口(viewport),大小等于屏幕可视区域。
  • 对于非根元素,包含块的计算主要基于其最近的定位父元素,如果该父元素是position: relativeabsolutefixedsticky,那么包含块就由它决定。
  • 对于absolute定位的元素,包含块是它最近的定位祖先元素的内边距区的边缘
  • 你还得知道:transformwill-change等属性也能改变包含块的行为。

最后再来看一些有趣的例子:

元素 包含块
html 初始包含块(视口)
body html
div1 body
p1 div1
p2 div1
em1 p2
strong1 p2

如果你对div1进行了定位,比如加上position: absolute;,那么它的包含块就会变成初始包含块,而不再是body了。

1