布局和包含块

元素的大小和位置通常会受到其**包含块**的影响。大多数情况下,包含块是元素最近的**块级**祖先的**内容区域**,但这并非总是如此。在本文中,我们将探讨确定元素包含块的因素。

当用户代理(如您的浏览器)布局文档时,它会为每个元素生成一个盒子。每个盒子被分成四个区域

  1. 内容区域
  2. 填充区域
  3. 边框区域
  4. 外边距区域

Diagram of the box model

许多开发人员认为元素的包含块始终是其父元素的内容区域,但这并不一定正确。让我们研究一下决定元素包含块的因素。

包含块的影响

在了解是什么决定了元素的包含块之前,了解它为什么重要是有帮助的。

元素的大小和位置通常会受到其包含块的影响。应用于绝对定位元素的**width**、**height**、**padding**、**margin**和偏移属性的百分比值(即其**position**设置为**absolute**或**fixed**)是根据元素的包含块计算的。

识别包含块

识别包含块的过程完全取决于元素的**position**属性的值

  1. 如果**position**属性是**static**、**relative**或**sticky**,则包含块由最近祖先元素的内容框边缘形成,该祖先元素要么是**块级容器**(例如内联块、块或列表项元素),要么**建立格式化上下文**(例如表格容器、flex 容器、网格容器或块级容器本身)。
  2. 如果**position**属性是**absolute**,则包含块由最近祖先元素的填充框边缘形成,该祖先元素的**position**值不为**static**(**fixed**、**absolute**、**relative**或**sticky**)。
  3. 如果**position**属性是**fixed**,则包含块由**视窗**(在连续媒体的情况下)或页面区域(在分页媒体的情况下)建立。
  4. 如果**position**属性是**absolute**或**fixed**,则包含块也可以由最近祖先元素的填充框边缘形成,该祖先元素具有以下任何属性
    • **filter**、**backdrop-filter**、**transform**或**perspective**值不为**none**。
    • contain 属性的值为 layoutpaintstrictcontent(例如 contain: paint;)时。
    • container-type 属性的值不为 normal 时。
    • will-change 属性的值包含一个属性,该属性的非初始值会形成包含块(例如 filtertransform)时。
    • content-visibility 属性的值为 auto 时。

注意: 根元素(<html>)所在的包含块是一个称为初始包含块的矩形。它的尺寸与视窗(对于连续媒体)或页面区域(对于分页媒体)的尺寸相同。

注意: 浏览器在 perspectivefilter 属性是否参与形成包含块方面存在不一致。

从包含块计算百分比值

如上所述,当某些属性被赋予百分比值时,计算值取决于元素的包含块。以这种方式工作的属性是盒模型属性偏移属性

  1. heighttopbottom 属性根据包含块的 height 计算百分比值。
  2. widthleftrightpaddingmargin 属性根据包含块的 width 计算百分比值。

注意: 一个块级容器(例如内联块、块级或列表项元素)要么只包含参与内联格式化上下文的内联级盒子,要么只包含参与块级格式化上下文的块级盒子。一个元素只有在它包含块级盒子或内联级盒子时才是一个块级容器。

一些示例

我们所有示例的 HTML 代码如下:

html
<body>
  <section>
    <p>This is a paragraph!</p>
  </section>
</body>

以下每个示例中只更改 CSS。

示例 1

在本例中,段落是静态定位的,因此它的包含块是 <section>,因为它是最接近的祖先,并且是一个块级容器(由于 display: block)。

css
body {
  background: beige;
}

section {
  display: block;
  width: 400px;
  height: 160px;
  background: lightgray;
}

p {
  width: 50%; /* == 400px * .5 = 200px */
  height: 25%; /* == 160px * .25 = 40px */
  margin: 5%; /* == 400px * .05 = 20px */
  padding: 5%; /* == 400px * .05 = 20px */
  background: cyan;
}

示例 2

在本例中,段落的包含块是 <body> 元素,因为 <section> 不是一个块级容器(由于 display: inline),并且没有建立格式化上下文。

css
body {
  background: beige;
}

section {
  display: inline;
  background: lightgray;
}

p {
  width: 50%; /* == half the body's width */
  height: 200px; /* Note: a percentage would be 0 */
  background: cyan;
}

示例 3

在本例中,段落的包含块是 <section>,因为后者的 positionabsolute。段落的百分比值受其包含块的 padding 影响,但是如果包含块的 box-sizing 值为 border-box,则不会发生这种情况。

css
body {
  background: beige;
}

section {
  position: absolute;
  left: 30px;
  top: 30px;
  width: 400px;
  height: 160px;
  padding: 30px 20px;
  background: lightgray;
}

p {
  position: absolute;
  width: 50%; /* == (400px + 20px + 20px) * .5 = 220px */
  height: 25%; /* == (160px + 30px + 30px) * .25 = 55px */
  margin: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
  padding: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
  background: cyan;
}

示例 4

在本例中,段落的 positionfixed,因此它的包含块是初始包含块(在屏幕上,是视窗)。因此,段落的尺寸会根据浏览器窗口的大小而变化。

css
body {
  background: beige;
}

section {
  width: 400px;
  height: 480px;
  margin: 30px;
  padding: 15px;
  background: lightgray;
}

p {
  position: fixed;
  width: 50%; /* == (50vw - (width of vertical scrollbar)) */
  height: 50%; /* == (50vh - (height of horizontal scrollbar)) */
  margin: 5%; /* == (5vw - (width of vertical scrollbar)) */
  padding: 5%; /* == (5vw - (width of vertical scrollbar)) */
  background: cyan;
}

示例 5

在本例中,段落的 positionabsolute,因此它的包含块是 <section>,它是具有非 nonetransform 属性的最接近的祖先。

css
body {
  background: beige;
}

section {
  transform: rotate(0deg);
  width: 400px;
  height: 160px;
  background: lightgray;
}

p {
  position: absolute;
  left: 80px;
  top: 30px;
  width: 50%; /* == 200px */
  height: 25%; /* == 40px */
  margin: 5%; /* == 20px */
  padding: 5%; /* == 20px */
  background: cyan;
}

另请参阅