CSS 排版与正常流 —— 重学CSS

855 阅读45分钟

同学们好,我是来自 《技术银河》的 💎 三钻

这一周我们重新回到《重学 CSS》系列,之前的文章中我们重新学习了《 CSS 选择器 》和《 CSS 语法与规则 》。接下来我们就一起来讲讲 CSS 里面的排版与正常流。

在讲解 CSS 当中的排版正常流的时候,我们会按照属性的一些逻辑关系来分成几个部分来讲解与学习。

盒 ( Box )

讲到排版,我们需要引入的第一个概念就是 ""。之前我们在《模拟浏览器》和之前的一些 CSS 的文章中都讲到了排版相关的概念。

而我们真正去讲到排版的时候,我们需要用到的单位一定就是 ""。

在真正进入详细了解 "盒" 的概念之前,我们先来做认识一下 3 个比较容易混交的概念。

  1. 标签 ( Tag ) —— 源代码
  2. 元素 (Element) —— 语义
  3. 盒 ( Box ) —— 表现

HTML 代码中可以书写开始标签,结束标签,和自封闭标签

标签是一个源代码的概念,所以方式我们提到在 HTML 代码中写的肯定都是标签。

一对起止标签,表示一个元素

元素是存在我们脑子里的一个概念,它是语义领域的一个概念,所以一对起止标签它一定是表示一个我们脑子里面的概念。

DOM 树中存储的是元素和其他类型的节点 ( Node )。

DOM 树中存储的不全是元素,因为DOM 树中存储的东西叫节点 Node,所以元素只是是节点的一种。

比如说我们的文本节点也是节点,但他并不是元素。再比如我们的注释节点,它也是节点但是它也不是元素。当然还有 CDATA 节点,还有 processing-instruction,DTD等这些都是会存入 DOM 树的,当时它们都并不是元素。

很多同学的理解,DOM 树中存储的都是元素,不过这样也没有错。因为其他的节点相对来说都没有那么重要。

CSS 选择器中的是元素

其实这里还可以加一个 "或",在《CSS 选择器》中讲到的,CSS 选择器选中的是元素或者是伪元素

CSS 选择器中的元素,在排版时可能产生多个

这个地方是大家需要注意到的一个概念,CSS 选择器选中的元素,它不一定和盒是一一对应的关系。它有可能是一对多的关系的。但是有盒一般来说必定是有对应的元素的。我们不可能无中生有产生一个元素,即使是号称是无中生有的伪元素也是依附于一个选中的元素产生的。

排版和渲染的基本单位是

在我们的《模拟浏览器》的实现过程中,我们的排版盒渲染都是直接拿元素当盒去用了。但是这是一个很粗糙的做法,在实际上我们很多元素都会产生多个盒。

比如说 inline 元素就会因为分行而产生多个盒。又比如说带有伪元素,被伪元素选择器选中的元素也会生成多个盒。所以我们排版盒渲染的基本单位都是盒

盒模型

既然我们讲到盒,我们都会讲到大名鼎鼎的 "盒模型"。我相信很多同学都知道盒模型,并且也学习过盒模型。但是也有很多同学可能没有理清楚摸透这个概念,所以就会导致不知道什么是盒模型,更不知道""这个概念是从何而来。

上面我们已经讲清楚了盒是从,标签到元素,到 CSS 选择器到如何产生了盒。所以对盒的来龙去脉我们都很清楚了,所以这里我们就可以开始详细的去了解盒模型的概念。

盒模型是我们排版的时候所用的一种基本单位

盒模型中的"盒",不光是有一个宽和一个高。不像在《模拟浏览器》部分里面那样非常的好算,其实它还是挺复杂的。

盒模型是一个多层的结构,从里面到外面分为:

  1. 最里面就是content,也就是我们的内容
  2. content 到 border 之间有一个圈空白,这个圈叫做 padding,也就是内边距
  3. Border 的外面又有一个圈空白叫 margin,也就是外边距

  • padding 主要影响的是盒内的空间 —— 主要决定盒内的空间排布,也就是 content 区域的大小
  • margin 主要影响的是盒外的空间 —— 决定了盒周围空白区域的大小

盒模型里面的 宽 (width) 是有讲究的,盒子的宽度是有可能被 box-sizing 属性所影响的。最常见的两个值就是:

content-box

设置的 width 属性只包含 content 的内容的空间。也就是说:

盒子占用的空间 = content 的大小 + padding 的大小 + border 的大小 + margin 的大小

怎么听起来就是一脸懵的感觉。😂

其实更接近人类的理解就是,我们在 CSS 中设置的 width属性只对最里面的 content的空间有效。其余的 paddingbordermargin 都会叠加到盒子的占用空间。

那这个为什么程序员都说这种盒子是 "反人类" 的呢?如果很早起就接触到 HTML 和 CSS 的同学应该都知道这么一个让人痛不欲生的场景:

在排版时候我们明明设置好这个盒子的宽度,但是最后加了 border 和 padding 就让盒子 "变大" 了。所以最后我们要反过来重新计算 width 属性来保证这个盒子是我们想要的宽度。

对!可能很多用习惯 box-sizing: border-box 的同学,早就忘记了这些痛苦的日子了。但是事实上这个设计理念就是有点反人类的。所以后面就打了一个补丁来拯救我们程序员,加入了 border-box。从此之后程序员就又可以开心的敲代码了。

border-box

使用border-box,我们的 width 就包含了 padding 和 border 的尺寸了。回去看看我们盒模型的图,我们可以看到border-box的黄色线括着的区域,它所占据的空间和范围。

这样当我们给一个盒,padding 和 border 的时候,就不会影响我们给予盒子的 width。这样我们就可以保证我们盒子在没有 margin 的时候它所占据的空间就是与我们 width 一致的。

呈现出来的效果就是 padding 和 border 都会往内挤压空间,而不会影响盒子的宽度。

这里我们就讲完盒模型了,我们就发现它所影响的属性就是 marginpaddingborderbox-sizing这几个属性。这些都是影响我们盒模型的总体尺寸,在排版中会影响着这个盒模型所占据的范围。

正常流

CSS 的排版其实是有三代的排版技术的:

  • 第一代就是正常流
  • 第二代就是基于 Flex 的排版
  • 第三代就是基于 Grid 的排版
  • 结合最近推出的 CSS Houdini,可能更接近的是 3.5 代,它是一种完全自由的,允许使用 JavaScript 干预的排版

目前主流都是在使用 flex 布局。相比 flex,其实正常流并没有变得更简单,反而是更复杂了。

不过挺有意思的是,flex 它比前面的第一代的排版技术要简单,比他后面一代的 grid 也简单。个人认为 flex 是最简单并且最容易理解的一代排版技术。

正常流呢,其实它能力最差,但是反而他的机制很复杂。

排版

如果我们看到上面这个图,可能有一些同学知道是什么,有一些同学完全不知道他在干什么。其实这个就是 80 年代印刷厂工人在进行排版工作。

这个传统的排版技术,其实与我们现在网页的layout是息息相关的。在很多文章中,我们会把layout翻译成排版,有时候也会翻译成布局。但是我个人也觉得翻译成排版是最贴切的。因为 CSS 当中的layout是源自于传统的排版技术。

传统的排版方式,我们需要先把字版放入一个一个字框里面,按照文字的顺序排列好,然后再把这些字框一个一个的排列进我们的排版框里面。 所以所谓排版就给我们所有可见的东西放到正确的位置上去。而在 HTML 里面,我们是有 "盒" 这样一个东西,在 CSS 的排版里我们只排两样东西

  1. 文字

一切 CSS 的排版,都不会逃出这文字这样两东西。所以我们的排版就是给每一个文字安排到正确的位置上,然后给每一个盒安排到正确的位置上。

在不考虑盒模型的情况下,我们需要关注的就是位置和尺寸,所以排版中并没有什么特殊的内容。

字排版

我们在进入正常流之前,我们一起来先思考一下我们写字的时候是怎么写的。

其实我们写字的时候,某种意义上讲也是一个排版的过程。很多同学们小时候写作文的时候都会用到上面这种有格子的稿纸。我们都记得这个作文稿纸上都是有一个一个的格子的,其实这相当于一个已经有了排版。

如果在我们稿纸上没有格子呢,我们就要自己来决定每一行要写多少个字,每一个字有多高,然后怎么分段呢?等等这些问题,都是在我们小时候写字时需要关注的。

那么我们来总结一下写字有哪些规则呢?

  • 从左到右书写 —— 我们都是依次从左向右书写作文的
  • 同一行写的文字都是对齐的 —— 写中文的时候我们是对齐到格子的顶和底,写英文的时候,像 e,a 这些字母它就有一条基线要去对齐。所以小时候英文的书写本都是四线本。
  • 一行写满了,就换到下一行

其实以上的这些规则也就是我们说的 "正常流 ( Normal Flow )",所以正常流为什么正常呢?因为正常流与我们平时书写文字的习惯一致。无论是中文也好,英文也好,它们都是遵循这种自然的排版方式的。

有一些我们早期进入前端的同学,就会发现其实正常流里面,有很多特别不正常的东西,特别的反直觉,反人类的东西。为什么这么奇怪的东西要叫正常流呢?

很负责任的告诉大家,其实一点都不奇怪。如果我们知道前端的 HTML 和 CSS 的排版概念都是源自于专业排版知识,而这些排版使用方式如果出现在我们的书籍里面,我们都会觉得挺自然的。但是真正让我们去理解这些知识,我们都会觉得很困难。

这个就可以追溯到 HTML 最早期整个的排版设计,都是从文字出版行业过来的专家所做的。所以它使用的思路都是那个时代的一个专业的思路。跟我们自然人脑子里面的理解,可能就会有一些差异了。

正常流排版

接下来我们就正式进入正常流的排版讲解。

正常流排版的整个过程,与实现 flex 的过程比较类似,有这几个步骤:

  • 收集盒与文字进行
  • 计算盒与文字在行中的排布
  • 计算行与行之间的排布

我们发现其实这个与我们 flex 的排版非常的像。其实我们会发现所有的排版算法,基本上都是差不多的。不论是哪个软件,哪个规则,它们都是这么几个步骤。

接下来我们来看看具体的一个排布规则:

当文字和盒在一行里面的时候,它们是会从左向右排布的。假设我们先不考虑 writing-mode 的事情,这里所说的从左向右排就意味着文字和盒有一个对齐的规则。这个就比我们小时候写作文的那个格子要复杂了。

在写作文的格子稿纸上,我们是不需要考虑图片和其他元素与我们的文字混排的情况的。我们在写作文的时候不会写着写着在旁边画一个插图什么的。但是我们发现书里面是有的,特别是专业的书籍基本上都是图文内容。

这些书里面都是写着写着就会插入一个图表,这些图表有的是在行内放一个小的图片,有的是里面加一个小图标。这些都是行内盒,更准确的说这叫 inline-level-box,就是行内级别的盒。

还有一些我们需要插入一个大图,比如说一个统计的数据,因为这种一般来说高度都比较高,放在一行里面会很奇怪。所以这个时候我们会让它单独占一行。这种类型的盒,我们就把它称为 block-level-box ,也叫块级盒。

所以文字和 inline level 行内的盒排出来的行,我们就叫做 "行盒 ( line-box )",然后一个自然的正常流,整体来看就是一个个的 line-boxblock-level-box 的从上到下的摆布。如果没有 block-level-box,那么都是行盒从上到下排布。而每个行盒的内部都是从左到右的排布。

其实上面讲到的两种情况都是有一个名字的。

  • 块级排布的我们就叫 BFC —— Block level formatting context (块级格式化上下文)
  • 行内排布的我们就叫 IFC —— Inline level formatting context (行内级格式化上下文)

很多在比较早期学习前端,甚至在一些 "0 基础" 的班里面学前端的同学,都会听说过 "块级元素" 和 "行内元素" 两种说法。其实与我们讲到的 BFC 和 IFC 就是这个概念最原本的意思。

行级排布

小时候我们在学习英文的时候,就会使用过这种 4 线本的格式来书写英文。

我们可以注意到在书写 example 这个英文的时候,我们可以看到 exam 都是在一行之内的。但是 pl 就不一样了。p 的尾巴是稍微往下突出的,而 l 它的头部是稍微往上突出的。

所以英文的字母呢,总是会有一些字母会往上或者往下伸出一块的。这个也是英文书写的一些规范,因为遵循这些规范才能让我们的字母在书写的时候好看。基本上所有字母的下缘都是需要对准一条线的,那就是倒数第二条钱(在图片中的黄色的线)。

这条线也叫 "基线",而这个规则就叫做 "基线对齐"。

其实各国的文字都是有一条基线对齐的规则,但是不同的国家的文字依赖的基线的位置不一样。比如说中文需要和英文混排的话,就会出现一个基线的偏移。

像中文这种类型的字,都会以上文下文这种边缘作为基础线去对齐的。但是我们也是可以认为这些类型的文字也是基于 基线 (baseline) 对齐的。我们可以理解为它们只是带了一定的偏移。

字形定义

接下来我们一起来了解一下,一个字形它是怎么样去定义的。在之前的文章中我们有讲到一些字符集这方面的基础知识。其实一个字符就是一个码点,意味着它的形状是由我们的字体来决定的。

下面的两张图是来自于一个著名的 C++ 的底层库,叫做 freeType。这个库就是处理各种字体文件成为抽象,并且它是一个开源界大部分的软件都会去使用的一个字体库。

这个库中,它从字体中抽象出这么一个定义:

任何一个文字,都是有一个宽和高,除此之外还有一条基线的定义。(如何没有这个基线的定义,我们的字体是不成立的,也没有办法用它来进行排版)。这个定义就叫做 Glyph Metrics

我们先来看看第一张图:

首先我们看到 origin,也就是一个字体的原点。而原点标识的位置就是我们文字的基线的位置,而这个文字都是以基线作为原点的坐标,然后再用这个坐标来定义这个文字的位置。

所以说基线就是在 x = 0 这个横向的坐标,然后这个文字就会有一个 xMax 和一个 yMaxxMax 就是文字最尾端的坐标位置,而 yMax 就是文字最顶端的位置。

然后文字也有相反的两个值 xMinyMin,而这两个值就是刚好相反,是文字开始的位置和底部的位置。

也可以理解为从底缘的基线到文字的最顶端的距离,这就是 yMax。而底缘的基线到文字的最底端的距离就是 yMin。而 xMinxMax,从原点到文字开始和文字结束的距离。

最后这里面的 advance 就是整个字占用的空间。

以上讲的就是横向排版的时候,而纵排又有另外一套的逻辑:

我们需要理解一些文本这样的基础概念,然后我们才能更好的理解文本和盒的混排。

行模型

我们大致了解了这个文字在字体里面是如何定义之后,我们就可以来讲讲 CSS 里面的行模型了。这里我们重点讲 5 条线,其他还有一些线和位置例如 subsup 这种我们就不去讲了。

这个 base-line 和文字的顶缘和底缘,分别叫做 base-linetext-toptext-bottom。就是图中的一条黄线和两条绿线。

结合刚刚讲到的,base-line 就是用来与英文字母对齐的。然后这里面有一个 text-top 和一个 text-bottom,如果我们的字体大小不变,这两个线是不会变的。

如果我们使用多个文字的字体混排的话,这个 text-toptext-bottom 是由 fontSize 最大的一个字体决定的。然后这个文字的上缘和下缘可以理解为两条固定的线。

如果行高是大于文字的高度的时候,我们就会有 line-topline-bottom ,就是在图中的两条白色的线。

好!如果我们只有文字的话,这个行模型就这样子排布的。但是我们一旦涉及到跟盒的混排,就会涉及到一个 line-topline-bottom 的偏移问题。

当我们的盒足够大的时候,我们的盒子是从 text-bottom 去对齐的,所以它就有可能把这个高度撑开。这个时候 line-top 就会从虚线的位置,移动到了白色的实线的位置。(看上面的图)

这个现象也是在正常流当中,处理行模型中非常麻烦的一个现象。因为盒的先后顺序和尺寸都是影响 line-topline-bottom 的位置。但是盒是不会影响 text-toptext-bottom 的。

代码演示

接下来我们来一起看看在浏览器中的实际效果是怎么样,需要在浏览器上看效果,我们就需要写一段代码了。

  1. 我们先建立一个 wrapper 把我们的内容包裹起来,然后给一个背景颜色
  2. span 标签加入文字
  3. 在用 div 标签建立一个元素,然后给予这个元素 display: inline-block,把元素设置成行内盒
  4. 最后我们加入基线的元素 base-line,用这个来展示我们的基线是在哪个位置
<style>
  .wrapper {
    margin: 2rem;
    font-size: 50px;
    line-height: 100px;
    background-color: #2d2f42;
  }
  .base-line {
    overflow: visible;
    display: inline-block;
    width: 1px;
    height: 1px;
  }
  .base-line div {
    width: 1000px;
    height: 1px;
    background: fuchsia;
  }
  .text {
    color: #ddd;
  }
  .inline-box {
    line-height: 70px;
    width: 100px;
    height: 150px;
    background-color: aqua;
    display: inline-block;
  }
</style>

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box"></div> <!-- 行级盒 -->
</div>

最后呈现出来的效果如下:

这里我们发现,我们的行内盒是默认与基线对齐的规则。也就是说盒的下边缘会和文字的基线去做对齐

好,如果我们在盒子里面加文字又会怎么样呢?我们来试试看。

这里我们在盒里面加入一个 b 的文字。

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box">b</div> <!-- 行级盒 -->
</div>

最后呈现出来的效果就是这样的:

这里我们会发现盒子的对齐的位置发生了变化。盒的基线变成了它里面文字的最后一行的基线。也就是说,当一个盒子里面有文字的时候,这个盒子的对齐就会基于里面文字的基线做对齐。

这里如果我们在 b 的下一行加一个文字 c,我们又会发现另一种现象。

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box">b<br/>c</div> <!-- 行级盒 -->
</div>

这个是特别需要注意的问题,行内盒 inline-block 的基线是随着自己里面的文字去变化的。所以说大部分情况下是不建议大家给行内盒使用基线对齐的。

所以我们在使用行内盒的时候就需要给一个 vertical-align,属性值我们可以给 topbottom或者是middle都是可以的。

我们先看看 vertical-align: top —— 顶缘对齐

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: top">b<br/>c</div> <!-- 行级盒 -->
</div>

top 就是跟行的顶缘对齐,因为我们的外包框给的是 100px,所以它会把这一行撑开。

然后我们来看看 vertical-align: bottom —— 底缘对齐

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: bottom">b<br/>c</div> <!-- 行级盒 -->
</div>

与顶缘对齐不一样就是这个时候,盒就会从下面往上撑开。

最后我们看看 vertical-align: middle —— 中线对齐

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: middle">b<br/>c</div> <!-- 行级盒 -->
</div>

顾名思义,这个时候盒就会与文字的中线对齐。

其实这里我们还可以让盒与文字的顶缘和底缘对齐,也就是 text-toptext-bottom

我们来看看 text-bottom 是怎么样的:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行级盒 -->
</div>

盒一样会把上边缘盒下边缘撑开。

如果我们看到上面的图,紫色的线就是我们的基线,但是我们也可看看文字的中心线顶缘线底缘线的位置。我们只需要改动 base-line 这个 div 元素的 vertical-align 属性即可:

我们先看看中线:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: middle"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行级盒 -->
</div>

顶缘线:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: top"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行级盒 -->
</div>

底缘线:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: bottom"> <!-- 对齐基线 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字内容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行级盒 -->
</div>

这里我们可以观察到,我们的盒是把我们的 顶缘线 (top-line) 往上撑开了的,所以也证明我们行内盒是会把顶缘或者下缘撑开的。

为了让大家可以更加直观的看到顶缘和底缘被撑开的现象,我们可以加多一个行内盒,同时把顶缘和底缘同时撑开。

<div class="wrapper">
      <div class="base-line">
        <div></div>
      </div>
      <span class="text">Hello world 中文</span>
      <div class="inline-box" style="vertical-align: text-bottom">
        b
        <br />
        c
      </div>
      <div class="inline-box" style="vertical-align: text-top">
        b
        <br />
        c
      </div>
    </div>

这里我们可以明显发现,我们外包框的高度已经被撑开了,所以外包框的高度已经不是等于行内盒的高度了,应为它的顶缘和底缘线都被撑开了。这个显现就是行内盒不同的 align 对齐所导致的,所以不同的 vertical-align 对整个行的高度是有比较多的影响的。所以相对于 flex 布局,flex 就只需要去考虑最高的一个元素,但是在正常流里面的行模型还是比较复杂的。

最后我给大家附上这段代码的演示:

查看效果查看代码喜欢的同学 🌟star 一下谢谢!

块级排布

接下来我们来一起了解一下,正常流的块级排布。我们在上一部分已经了解了块级排布了。接下来我们一起来了解行级与块级的盒之间是如何排布的。

之前讲到的 BFC 和 IFC 的基本定义的时候,我们对块级排布已经有一个基础的认识了。但是呢正常流当中还有两个非常复杂的机制,我们需要先了解一下。

Float 和 Clear

Float 和 Clear 也称为 浮动清除浮动。首先浮动元素严格来说已经脱离了正常流,当时他又依附于正常流去定义的一类排布方式。

首先我们先来讲一下 float 的基本规则。根据 W3C 的标准,float 可对它有一下定义:

float 元素可以先排到页面的某一个特定的位置,同时可以当它是正常流里的元素。然后如果它的属性中有 float 的时候,这个元素就会朝着 float 属性定义的方向去挤。

假设元素加入一个 float: left 属性,这个时候,这个元素就会往左边去挤,如下图:

通过上面这个动画,我们会发现,原来已经存在的文字位置就被浮动了的元素所 "盖住" 了。所以呢这个时候就会根据 float 所占据的区域去调整行盒的位置。因为计算位置的时候我们还没有去计算每一个文字具体的位置,所以说理论上来讲这个地方的文字是没有重排的。

所以当一个元素变成了浮动时,它所占据的位置的原本内容,就会根据 float 之后占据的宽度来进行调整。而 float 显著的特征就是,它会影响我们生成这些行盒的尺寸。

所以在文字调整了之后我们最终看到浏览器呈现的效果是这样的:

float 不止会影响自己所在的行,凡是它的高度所占据的范围内的所有行盒都会根据 float 元素的尺寸调整自己的大小。如果超出了这个 float 范围的就不考虑了。

如果我们有两个 float 元素的时候,还会出现一种情况。假设我们现在有两个 float 元素,一个是 float: right 在右边,然后再后面又加入了一个 float 元素,而这个新的 float 元素一样也是向右浮动的。

这个时候我们会发现这个新的 float 元素也会受到上一个 float 元素影响,新的 float 元素是无法占据上一个 float 元素的位置。这时候的这个现象就是 float 元素相堆叠的效果

当然如果我们继续添加 float 元素,并且也是浮动一样的方向的时候,都会受到上面多个 float 元素的影响并且继续堆叠。

接下来我们来看看一段实例代码:

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
  }
  .wrapper {
    padding: 1.5rem;
    background: #2d2f42;
    color: #ededed;
  }
  .purple {
    color: fuchsia;
  }
</style>

<div class="wrapper">
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

这里面 「float-放置的位置」 就是原本这个 float 元素的代码所放置的位置。但是经过了 float 之后,所有的 float 元素都会被挤到最右边的位置。最后所有行盒的生成都会围绕着所有的 float 元素,同时它们都不会占据 float 元素的空间。

这个时候当我们把外框的宽度增加的时候,就会发现这些 float 元素就会出现堆叠的现象。

看到这里肯定很多同学说觉得 "这样排版不行呀,老板肯定不收货呀","UI 设计师肯定要吊打我们啦"。是的这样的排版确实是不正常的,而且一般来说也没有这么排版的需求呢。

所以这里我们可以给 float 加一个 clear 属性,这样我们就可以让它们强制换行了。

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
  }
  .wrapper {
    padding: 1.5rem;
    background: #2d2f42;
    color: #ededed;
  }
  .purple {
    color: fuchsia;
  }
</style>

<div class="wrapper">
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box" style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box" style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"  style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

查看效果查看代码喜欢的同学 🌟star 一下谢谢!

所谓的 clear 有的翻译成 "清除浮动",但是我觉得它不是清除浮动的意思。理论上来说它是 "找一个干净的空间来执行浮动" 的意思。

比如说 clear: right,就是要在右边找到一个干净的空间来执行这个浮动的操作。

所以看到上图的效果,当我们加入了 clear: right 之后,第一个后面的元素都不受第一个 float 元素的影响,各自找到一个干净的空间进行浮动。所以 clear 是有分 leftright,指的是向左或者右找干净的空间进行浮动。

虽然说 float 这种浮动布局给我们带来了不少的麻烦,但是 float 的堆叠、自动换行(或者使用 clear 去换行)的行为是可以帮助我们去进行一些有用的布局的。因为我们的正常流的布局在早年没有 flex的情况下,正常流的布局下完成一些著名的 CSS 布局需求的时候是非常的困难的。

所以大家都产生这种使用 float 来代替正常流的 inline-block 来进行布局的技术

接下来我们看看下一段代码实例:

这里我们给每一个元素都加上一个 float,这时候元素跟元素之间的表现就很像一个正常流了。因为 float 元素它占满了之后还是会换行的。那么我们来看一下代码:

<style>
  .float-box {
    margin: 10px;
    float: left;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
    text-align: center;
    line-height: 100px;
    font-size: 50px;
  }
</style>

<div class="float-box">1</div>
<div class="float-box">2</div>
<div class="float-box">3</div>
<div class="float-box">4</div>
<div class="float-box">5</div>
<div class="float-box">6</div>
<div class="float-box">7</div>

这里我们可以看到,float 是完全可以变成和正常流一样排成一行的,当我们缩小宽度的时候,不够位置的元素就会自动换到下一行。

这个时候如果我们想执行一个强制换行怎么办呢?这个时候我们就可以拿出我们的 clear。比如说我们想在 3 后面开始强制换行,我们就可以在 4 的浮动元素上加入 clear: left 即可。

这里要注意的是,float 它是不认 <br/> 的,如果我们在一个 float 元素的后面加入 <br> 是无法让他强制换行的。因为 br 是正常流的换行,对 float 是没有效的。

<style>
  .float-box {
    margin: 10px;
    float: left;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
    text-align: center;
    line-height: 100px;
    font-size: 50px;
  }
</style>

<div class="float-box">1</div>
<div class="float-box">2</div>
<div class="float-box">3</div>
<div class="float-box" style="clear: left">4</div>
<div class="float-box">5</div>
<div class="float-box">6</div>
<div class="float-box">7</div>

我们平时做布局的时候,基本上我们都会使用盒而不是文字。我们很少有真正意义上需要像原始的 float 设计一样用图文混排的设计(就是向我们看的图书一样的布局方式)。在网页上布局,一般来说我们都是针对同一个级别,我们都是几个盒子排成正确的形状来完成页面上的布局。

所以说 float 这种布局的技术,在比较早期的时代是非常流行的。不过到了现在的前端技术时代,float 的布局方式已经完全被 Flexbox 技术所替代了。这里只是让大家知道一下,布局的历史里面有这么一个用法。

如果有同学遇到了一些需要维护非常古老的代码,就有可能看到这个技术。但是我不推荐大家当今还去使用这项技术。

查看效果查看代码喜欢的同学 🌟star 一下谢谢!

Margin 折叠

接下来我们来讲讲正常流里面的一种现象,叫做 margin 折叠。在 BFC 里我们的元素是顺次从上往下排的,但是顺次从上往下排的时候还是会受它的盒模型影响的。

就是有这么一个现象,在一个从上往下排布的 BFC 里面,有一个元素它有 margin,接着还有一个元素,它也有 margin。那么这个时候第二个元素它应该怎么排呢?

直接告诉我们应该是两个元素的 margin 是应该会叠加,如果第一个的 margin 是 10,第二个是 15,那么两个元素中间就是 10 + 15 = 25。对吧?不是的。

它们并不是把两个 margin 的空白都留出来。而是会让他们两个发生一个堆叠的这样的现象。最后叠出来的高度是跟最大的 margin 的高度相等的。如果一个是 10px,一个是 15px 的 margin,最后两个元素之间的空间就是 15px(使用了两个 margin 的最大值)。这个现象就是 Margin Collapse (留白折叠/边距折叠)

有些同学觉得真的是匪夷所思,根本不符合常理。但是其实这个是一个排版里面的要求,因为在我们的排版当中,任何一个元素,它的盒模型里面所谓的 margin "只是要求周围有这么多的空间是留白的,而不会说要求元素与元素之间的边距格子都有相对应的空白"。所以只要元素的周围的留白的空间够了,自然就是一个合理的排版方式。

这个可以说也是继承了印刷行业,古老的印刷行业的排版体系中,这种 Margin Collapse 是一个非常自然的现象和思路。

我们要注意,这种 Margin Collapse 只会发生在 BFC 里面。它不会发生在 IFC 或者其他的排版方式里面,比如说 flex、grid 等都不会有 Margin Collapse 的。所以只有正常流中的 BFC 会发生边距折叠!

其实我们单看 float和单看边距折叠,它们都不太难,我们但看 BFC 也不太难。但是如果我们三个现象叠加在一起,基本上就是我们古代前端最大的难题了。也就是说大家平时会看到一些资料里面讲,面试的时候遇到的 BFC 问题就都在这个三个现象相叠加的。

BFC 合并

接下来我们来认识一下正常流最困难的一部分,"BFC 合并"。在讲到具体的知识之前我们先来了解一下几个概念。

BFC 代表的是 Block Formatting Context (块级格式化上下文)

Block (块)

我们先大致了解一下 Block 里面有哪些:

  • Block Container —— 是在 CSS2.1 标准里面定义的
    • 里面能装 BFC 的盒
    • 能容纳正常流的盒,里面就有 BFC
  • Block-level Box
    • 外面有 BFC 的盒
    • 也就是说它能够被放入 BFC 的这种盒子里
  • Block Box = Block Container + Block-level Box
    • 就是上面两个之和
    • 里外都有 BFC 的盒

这里我们就逐个拆开来讲一下:

Block Container

Block Containter 基本上是一些 display 的效果:

  • block
  • inline-block
  • table-cell —— 里面都是正常流,但是 table-row 就不是 block cotainer 了,因为它里面是 table-cell,所以不可能是正常流
  • flex item —— display: flex 的元素不是 block container,但是 flex 的子元素 flex item 如果它们没有特殊的 display 属性的话它们都是 block container。
  • grid cell —— grid 也是有 cell 的,所有 grid 的 cell 默认也都是 block container
  • table-caption —— table 中有 table-caption (表格标题),它里面也是正常流

任何一个元素里面只要不是特殊 display 模式的,它里面默认就是正常流。

Block-level Box

我们大多数的元素的 display 的值都是有一对的。一个是 block-level 的,一个是 inline-level 的。

Block levelInline level
display: blockdisplay: inline-block
display: flexdisplay: inline-flex
display: griddisplay: inline-grid
display: tableDisplay:inline-table

在 display 的最新标准里面,已经把 block-levelinline-level 拆成了单独的属性。Display 会出现这个 innerouter,这里就是给 display 做了系统性的分解。

这里还有一种非常特殊的 display 属性叫 run-in。它跟着自己上一个元素来,有时候是 inline-level,有的时候是 block-level。这个属性我们知道就可以了,在工作中基本上没有见过有例子有运用到 run-in 这个属性。

Block Box

在标准里面有这么一个描述:"什么时候什么样的盒,会创建 BFC 呢?"

设立 BFC (Establish BFC)

以下情况下会 设立 BFC

  • floats —— 浮动的元素里面就是一个正常流,所以会创建 BFC
  • Abusolutely positioned elements —— 绝对定位的元素里面也会创建 BFC
  • block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes —— 就是这个 block container 但是不是 block box (也就是不是 block-level ) 会创建 BFC,包括以下:
    • inline-blocks
    • table-cells
    • table-captions
    • Flex items
    • grid cell
    • 等等...
  • and block boxes with overflow other than visible —— 就是拥有 overflow 属性,但是不是 visibleblock box 也会创建 BFC

所以设立 BFC 一共有 4 大类,但是要记住这些确实不太好记。不过换一种方式来理解就好记一点了。

默认这些能容纳正常流的盒,我们都认为它们会创建 BFC,但是只有一种情况例外:就是 block box 里外都是 BFC 并且 overflow 是 visible。用公式来记就是这个:"block box && overflow:visible"

这个其实是非常合理的,它里外都是 BFC 而且它的 overflowvisible,那不就相当于没有 BFC 了吗?所以这个时候会发生 BFC 合并。

BFC 合并

那 BFC 合并之后会发生什么呢?这里我们就需要了解两个最重要的 BFC 合并之后的影响:

BFC 合并与 float

因为 BFC 发生了合并,所以里面的行盒跟这个 float就有了一定的影响。

正常的来讲我们放一个 block box 它的 overflow 不是 visible,这个时候它会创建 BFC 并且整个 block box 放进 BFC 里面。那么整个就会受 BFC 影响,如果不创建 BFC它里面的行盒就会受 float 的影响。

下来我们用一段代码来看看其中的现象:

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: aqua;
    margin: 20px;
  }
  .text {
    background-color: #2d2f42;
    color: #ededed;
    overflow: visible;
    margin: 30px;
  }
</style>

<div class="float-box"></div>
<div class="text">
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

这里因为我们的 text 盒子给予了 overflow: visible,所以这个 overflow 属性值是不满足创建 BFC 的条件的。所以我们的文字的盒子就会像不存在一样,文字就会环绕着外面的 float-box 元素来进行排布。

如果我们把 overflow 的属性值改为 hidden 的话,那么 text 的盒子就会建立 BFC。

<div class="float-box"></div>
<div class="text" style="overflow: hidden">
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

这里我们就很明显的可以看见一个变化,text 的元素整体作为一个 block level 的元素被排进了 BFC 里面。也就是说这个时候 text 元素的宽度整个围绕着 float-box 元素来进行排布了。

查看效果查看代码喜欢的同学 🌟star 一下谢谢!

BFC 合并与边距折叠

刚刚我们讲到, BFC 只会发生在同一个 BFC 里面。如果创建了新的 BFC 的话,它就不会发生边距折叠。如果没有创建 BFC 的话,它就存在着一个同向边距折叠。

好,同样我们用一段代码的实例来试验一下:

<style>
  .box {
    width: 100px;
    height: 100px;
    background-color: aqua;
    margin: 20px;
  }
  .overflow-box {
    overflow: visible;
    background-color: #2d2f42;
  }
</style>

<div class="box"></div>
<div class="overflow-box">
  <div class="box"></div>
</div>

这里的两个 box 元素都给予了 100 x 100px 宽高,同时有一个 20px 的外边距。第二个 box 元素我们用一个含有 overflow 属性的 div 包着。首先我们默认给予这个 overflow 元素 overflow: visible,然后我们来看看会出现什么效果。

出来的效果显而易见,我们可以看到上面和下面的两个 box 元素的外边距发生了边距折叠现象。这里两个盒子就当做 overflow 这个元素不存在一般,最后他们两个的距离就是 20px。

这里做一个小实现,如果我们给 overflow 元素加入一个 margin-top: 30px,这个时候会对这两个 box 的距离发生影响吗?

答案是:会的!但是还是还是会出现 边距折叠 的现象,只是它们之间的距离变成了 30px,但是并不会变成 20+30+20 这样的效果。我们来看看运行效果:

<div class="box"></div>
<div class="overflow-box" style="margin-top: 30px">
  <div class="box"></div>
</div>

最后如果我们把 overflow-boxoverflow 属性改为 hidden, 这个时候它就会创建 BFC。这时候我们就会发现,边距折叠 现象就会消失,最终两个 box 元素的距离就是 30+20 = 50px。

<div class="box"></div>
<div class="overflow-box" style="margin-top: 30px; overflow: hidden;">
  <div class="box"></div>
</div>

"不知道这里发生了什么,但是觉得很棒!哈哈"

这里的 overflow-box 里面的 box 元素已经和外面的 box 元素不在同一个 BFC 里面了。所以它们之间不会再发生边距折叠现象。但是 外面的 overflow-boxbox 是处于同一个 BFC 中,所以它们两个依然会发生边距折叠现象。所以这个就是创建 BFC 对边距折叠这个行为的影响。

我是来自微信公众号:《技术银河》的三钻:"学习是为了成长,成长是为了不退步。坚持才能成功,失败只是因为没有坚持。同学们加油哦!下期见!"