布局进阶


包含块

什么是包含块?

描述:一个元素的框的尺寸和位置的计算有时相对于某个特定的长方形,称为该元素的包含块。

---W3C

元素的布局经常会受到包含块的影响,比如计算块级元素垂直方向上 margin 的百分比,又或者粘性布局的参考区域等,相关布局都会受到包含块的影响。

确定元素的包含块

一个元素的包含块和它的 position 属性息息相关:

  1. position 的值是 relativestatic 或者 sticky,该元素的包含块一般由它最近的块元素祖先的内容边界组成。
  2. position 值为 fixed 时,该元素的包含块就是视口区域。
  3. position 值为 absolute 时,包含块由该元素最近的 position 不是 static 的祖先的内容边界组成。

当然包含块的确定不止规则,比如把一个页面使用打印机打印,那么固定定位元素的包含块就是分页区域,详情点击这里

计算盒模型的百分值

  • heighttopbottom 的百分值参考的是包含块的高度。但是有一个特殊情况:

    • 当包含块的 高度不确定 时(比如高度由内容撑起时)并且当前元素 不是绝对定位时,上述属性的百分比会被当做 auto 处理。
  • widthleftrightpaddingmargin 的百分值参考的是包含块的宽度。

  • 粘性定位元素的偏移值 topbottomleftright 的百分比参考的是最近的滚动祖先,而非包含块,其余属性的百分比遵循上述规则。

border 属性并不支持百分值设置,所以上述没有介绍。

综上,如果你给一个固定定位的元素设置 margin-top : 50% 时,你会发现,它居然是根据视口宽度进行变化的。

行内格式化上下文 IFC

行内级元素进行横向布局时,会一起创建对应的行内格式化上下文 IFC,IFC 确定了行内级元素横向布局的规则与环境。

字体、大小和行高

  • font-family 不同,font-size 相同的字体表现出来的大小并不是完全相同。

  • line-height 决定的不是字体的高度,只是一个用来布局的行高。

  • line-heightnormal 和 100% 表现的行高不同,normal 往往比 100% 更高一些。

介绍两个字体的高度:

  • 字体高度:字体根据 font-sizefont-family 计算渲染出来的高度,可以使用一个 span 元素包裹文本,然后填充背景色看到。这个高度用来显示字体。
  • 字体行高:由 line-height 决定,这个高度用来布局。

演示这个沙箱:

IFC 环境中的框

看不见,摸不着的两个框:

  • 行内框(又称行内盒)

    • 行内框和盒模型并没有直接关系

    • 行内框包裹住了各个行内级元素或文本

    • 行内框的宽高

      • 如果是行内元素(或匿名文本),行内框的高度是字体行高。宽度那么由 font-size、文本长度、marginborderpadding 等相关属性在水平方向的排列后决定。
      • 如果是行内块元素,行内框的大小是其盒模型的总大小,即 margin + padding + border 加内容区。
  • 行框(又称行盒)

    • 行框和盒模型并没有直接关系
    • 行框包裹住了所在行中所有行内框
    • 高度:由行内框的排列后的高度决定,并不一定是内部最高的行内框高度
    • 宽度:默认情况下是包含块的宽度,如果受到浮动元素的影响,会减去浮动元素占据的宽度

演示这个沙箱:

块格式化上下文 BFC

常规流中纵向布局的环境和规则称作块格式化上下文 BFC,IFC 是一个或多个行内级元素参与创建的,而 BFC 一般是由元素直接创建的。

默认情况下,BFC 内的排版如下:

  • 独占一行:块级元素产生的各个盒子依次向下排列,每个盒子独占一行,(嵌套不会产生新的 BFC)。
  • 外边距重叠:块级元素之间的盒子在垂直外边距上会出现相互重叠;
  • 内容包含:BFC 内包含创建它的元素内部的所有内容;

创建 BFC

以下方式可以为 元素的内部 创建新的 BFC :

  • <html> 元素;
  • overflow 值不为 visible 的块级元素(overflow-x/y 其中一项不为 visible 即可,默认情况下,这种方式不能给 body 元素内部创建新的 BFC);
  • 行内块元素:display:inline-block
  • 浮动元素:flaot 不是 none:
  • 绝对定位和固定定位元素;
  • 其他(一些我们未学到或不常用的方式,又或者兼容性不佳,可以查看 这里 了解所有产生 BFC 的方式)

关于给 body 元素设置 overflow 不为 visible 时,不一定产生 BFC 的解释,在 W3C 中是这样介绍的:

用户代理必须把根元素上设置的 overflow 属性应用到视口上,当根元素是 html 或者 xhtml 元素,并且根元素具有 body 元素作为子元素时,如果根元素上的值为 visible,则用户代理必须把第一个这样的子元素的 overflow 属性应用到视口上。值 visible 用在视口上时解释为 auto。必须有 overflowvisible 的元素从中传递值。

--- W3C overflow

也就是说,默认情况下,给 body 元素设置 overflow 属性会应用到视口上,所以 body 元素不会产生新的 BFC 区域。

如果 html 元素的溢出样式不为 visible 时,那么给 body 元素的 overflow 设置非 visible 值时,就会产生新的 BFC 区域。

独占一行

外边距填充:

因为 BFC 内块级元素独占一行的特性,块级元素的宽度小于当前行时,其余部分会自动被 margin 填充。在这种情况下:

  • 设置水平方向其中一侧的 marginauto,外边距会在这侧进行填充;
  • 设置水平方向的 marginauto,这个块级元素就会水平居中。

边界对齐:

父子级的块级元素在水平上是按照父级内容边界和子级外边距的边缘进行对齐

就比如默认情况下的列表项,因为 ulol 元素拥有左侧的内边距,导致列表项元素会像右侧进行偏移。当 margin 设置为负值时,元素的外边距边界会收缩到元素内部,与父级元素对齐的位置也会产生改变。比如 margin-left: -100px

可以使用负值外边距,插入一个比父级宽度更宽但是水平居中的元素,比如:

<div class="outside">
  <p>这里是正常文本</p>
  <div class="inside">这里比父级元素更宽,并且在相对父级水平居中</div>
</div>

给上述代码插入以下样式:

.outside {
  margin: auto;
  width: 600px;
}
.inside {
  width: 700px;
  margin: 10px -50px;
}

外边距重叠

在常规流的 同一个 BFC 中,如果两个块级元素之间,没有边框 border、内边距 padding、行框或非空的内容区等阻挡它们的外边距,那么它们的外边距部分会发生重叠。外边距重叠又叫外边距合并,外边距折叠等等。

BFC 环境内的外边距重叠有以下三种情况:

  • 父子级
  • 兄弟级
  • 兄弟级中有空的块级元素

上面发生的重叠现象还会相互叠加,比如父子级产生重叠后,又与兄弟级产生重叠。

围绕外边距重叠的定义,有以下方式解决重叠的问题:

  • 让外边距不直接接触:在两个外边距之间添加 borderpadding、行框或非空内容区等;
  • 累加重叠部分:如果重叠了 20px,那么在其中一侧累加 20px 的外边距即可;
  • 建立新的 BFC 环境:不同 BFC 环境之间的元素不会产生外边距重叠。

是否一定需要解决外边距合并?

不一定,如果并没有影响到我们的布局,我们并不需要去处理外边距重叠,因为它并不是一个 bug,它只是 BFC 环境中块级元素的布局方式。

内容包含

BFC 内包含创建它的元素内部的所有内容。它会产生以下影响:

  • 外边距合并无法重叠到 BFC 外,会撑起当前 BFC 区域。
  • 浮动元素会撑起 BFC 区域。

关于浮动元素会撑起 BFC 区域:

基础布局的浮动布局小结中,使用 float 属性实现了一个横向列表,如:

ul {
  padding: 0;
  list-style: none;
}
li {
  float: left;
  width: 100px;
  height: 100px;
  margin: 20px;
  border: 1px solid black;
}

受到浮动元素的影响,在列表后面的文本会环绕这个列表布局,当时解决的方案是清除浮动。现在我们可以给 ul 元素建立新的 BFC 区域,这样 li 元素就会被 ul 建立的 BFC 区域包含,从而消除浮动给其他 BFC 区域文本造成的影响,比如给 ul 元素添加:

ul {
  overflow: auto;
}

overflow: auto 会产生新的 BFC 区域,因为我们没有给 ul 元素设置高度,所以也不会产生滚动条的副作用,此时 ul 元素将再次包含 li 元素,并挤压后续的文本到下一行显示。

杂项

  • 如果产生 BFC 的元素本身还是一个块级元素,并且周围存在浮动元素,那么它会紧靠浮动元素独占一行,不会因为浮动元素挤压换行。

  • 相对定位和粘性定位并不会产生新的 BFC 环境,所以它们都会受到浮动元素的影响,并且浮动对相对定位与粘性定位的影响在定位之前,而非定位之后;