前言
在开始本文之前先来看一个例子,下面一段简单的 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
- END -
关于奇舞团
奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。