理解 CSS 中的 Containing Block(包含块)
Table of Contents
前言 #
在开始本文之前先来看一个例子,下面一段简单的 html 代码,布局很简单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Css Containing Block</title>
<style>
body {
padding: 0;
margin: 0;
}
.parent {
height: 100px;
width: 100px;
background-color: red;
}
.child {
position: absolute;
top: 0;
right: 0;
width: 50%;
height: 50%;
background-color: yellow;
}
</style>
</head>
<body>
<div>
<div class="parent">
<div class="child"></div>
</div>
</div>
</body>
</html>
- 两个嵌套的 div,一个背景是红色,一个背景是黄色。
- div parent 设置的宽高分别是 100px、100px。
- div child 设置的宽高是 50%、50%。
让我们思考一下这段代码的运行效果应该是什么样子,然后看一下实际的运行结果是不是和我们思考的一样。
运行结果如下:
对于前端初学者来说可能会有这样的困惑,明明 child 的 width 和 height 分别设置的 50%、50%,那黄色背景的 child 的大小不应该是 width=50px、height=50px 吗?带着这个疑问我们来改一下代码如下:
给 parent 增加 transform: translate(20px, 20px);
.parent {
height: 100px;
width: 100px;
transform: translate(20px, 20px); // 添加transform
background-color: red;
}
保存之后在浏览器里运行,再看一下效果:
这次的运行效果和我们预期的一致了,黄色背景的 child width=50px、height=50px。
理解产生这个现象的原因,我们要知道 Containing Block。
transform 是 CSS 里的一个属性,它的取值很多,利用 transform 我们可以对元素进行旋转、缩放、平移以及元素歪斜(skew)
/* Keyword values */
transform: none;
/* Function values */
transform: matrix(1, 2, 3, 4, 5, 6);
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
transform: perspective(17px);
transform: rotate(0.5turn);
transform: rotate3d(1, 2, 3, 10deg);
transform: rotateX(10deg);
transform: rotateY(10deg);
transform: rotateZ(10deg);
transform: translate(12px, 50%);
transform: translate3d(12px, 50%, 3em);
transform: translateX(2em);
transform: translateY(3in);
transform: translateZ(2px);
transform: scale(2, 0.5);
transform: scale3d(2.5, 1.2, 0.3);
transform: scaleX(2);
transform: scaleY(0.5);
transform: scaleZ(0.3);
transform: skew(30deg, 20deg);
transform: skewX(30deg);
transform: skewY(1.07rad);
/* Multiple function values */
transform: translateX(10px) rotate(10deg) translateY(5px);
transform: perspective(500px) translate(10px, 0, 20px) rotateY(3deg);
/* Global values */
transform: inherit;
transform: initial;
transform: revert;
transform: revert-layer;
transform: unset;
Containing Block #
在介绍 Containing Block 之前,先来复习一下 CSS 盒模型,以标准盒模型为例,html 里的每一个元素都有一个盒子,盒子由 Content、Padding、Border、Margin 组成。标准盒模型的 Content 区域就是我们设置的宽、高大小。
在 CSS 中元素的大小和位置是由 Containing Block(包含块) 决定。在大多数情况下,一个元素的 Containing Block 就是离它最近的 block 级 的盒子的 Content 区域。比如我们把上面的例子再改一改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Css Containing Block</title>
<style>
body {
padding: 0;
margin: 0;
}
.parent {
height: 100px;
width: 100px;
background-color: red;
}
.child {
width: 50%;
height: 50%;
background-color: yellow;
}
</style>
</head>
<body>
<div>
<div class="parent">
<div class="child"></div>
</div>
</div>
</body>
</html>
运行结果:
这是我们最熟悉的。接下来我们看看什么情况下 Containing Block 不是离它最近的 block 级 的盒子的 Content 区域。
Containing Block 的改变由元素的 position 属性决定。
1、position 值为 static、relative、sticky #
如果 position 值为 static、relative、sticky,那么它的 Containing Block 就是离它最近的祖先块级元素的 Content 区域。或者是像 table、flex、grid 这种 formatting context。
position 默认值为 static
2、position 值为 absolute #
如果 position 值为 absolute,它的 Containing Block 就是离它最近的 position 的值不是 static (也就是值为 fixed, absolute, relative 或 sticky)的祖先元素的 Padding 区域。
举个例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Css Containing Block</title>
<style>
body {
padding: 0;
margin: 0;
}
.grandparent {
width: 300px;
height: 300px;
background-color: green;
position: absolute;
padding: 50px;
}
.parent {
height: 100px;
width: 100px;
background-color: red;
}
.child {
position: absolute;
width: 10%;
height: 10%;
background-color: yellow;
}
</style>
</head>
<body>
<div>
<div class="grandparent">
<div class="parent">
<div class="child"></div>
</div>
</div>
</div>
</body>
</html>
这个例子中,div child 的 width 为 grandparent 的 (width + paddingleft + paddingright) = (300+50+50)*10%=40px,height 同理。
3、position 值为 fixed #
如果 position 值为 fixed,在连续媒体的情况下 (continuous media) Containing Block 是 viewport。在分页媒体 (paged media) 下的情况下 Containing Block 是分 page area。关于连续媒体和分页媒体,我们的电脑显示屏一般是连续媒体,打印机一般是分页媒体。
连续媒体指的是那些可以无限延伸并且没有固定页面尺寸的媒体。常见的连续媒体包括计算机屏幕、投影仪、手机屏幕等。在处理连续媒体时,CSS 通常会自动将内容调整到适合媒体尺寸的布局,以便内容可以在用户设备上连续滚动或自适应显示。分页媒体指的是那些有固定页面尺寸、需要将内容分割成适合打印或显示在不同页面上的媒体。常见的分页媒体包括打印纸张、PDF 文件等。在处理分页媒体时,CSS 可以控制内容的分页、分栏和排版,以确保内容适合在每一页上显示,并提供良好的打印效果。
4、position 值为 absolute 或 fixed 的特殊情况 #
如果 position 值为 absolute 或 fixed,Containing Block 也可能是由满足以下条件的最近父级元素的 Padding 区域组成:
4.1 transform 或 perspective 的值不是 none 的父级元素 #
什么意思呢?我们把第二点中 position 值为 absolute 的例子改一下,给 parent 增加 transform: translate(20px, 20px);
, 这个时候 child 的 Containing Block 就是 parent 了,width 和 height 都为 10px。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Css Containing Block</title>
<style>
body {
padding: 0;
margin: 0;
}
.grandparent {
width: 300px;
height: 300px;
background-color: green;
position: absolute;
padding: 50px;
}
.parent {
height: 100px;
width: 100px;
transform: translate(20px, 20px);
background-color: red;
}
.child {
position: absolute;
width: 10%;
height: 10%;
background-color: yellow;
}
</style>
</head>
<body>
<div>
<div class="grandparent">
<div class="parent">
<div class="child"></div>
</div>
</div>
</div>
</body>
</html>
运行结果:
4.2 will-change 的值是 transform 或 perspective #
4.3 filter 的值不是 none 或 will-change 的值是 filter(只在 Firefox 下生效) #
4.4 contain 的值是 paint(例如:contain: paint;) #
4.5 backdrop-filter 的值不是 none(例如:backdrop-filter: blur(10px);) #
以上就是 Containing Block 就是最近父级元素的 Padding 区域。
总结 #
本文总结了作者对 Containing Block 的基本理解,帮助大家在写 CSS 布局的过程中遇到奇怪的现象进行问题排查。
参考资料 #
https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block
https://developer.mozilla.org/en-US/docs/Web/CSS/transform
https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flow_layout/Introduction_to_formatting_contexts
https://developer.mozilla.org/en-US/docs/Glossary/Continuous_Media