1.清除浮动

浮动的元素会影响其兄弟元素的位置,并可能导致父元素的高度塌陷(假如父元素没设置高度),因此需要清除浮动(带来的影响)。

1.1 方法

常用方法有:

  • 给浮动元素的父元素一个固定高度(不推荐)
  • 给浮动元素新增一个空的 div 兄弟元素,设置 clear:both
  • 给浮动元素新增一个 <br> 兄弟元素,设置 clear 属性为 all
  • 给浮动元素的父元素新增一个 after 伪类,设置该伪类和父元素:
div{
    zoom:1;
}
div:after{
    content:"";
    display:block;
    clear:both;
}    
  • 给浮动元素的父元素设置 display: table(触发BFC)
  • 给浮动元素的父元素也设置浮动(触发 BFC。不推荐)
  • 给浮动元素的父元素设置 overflow: hidden(触发 BFC)

1.2 原理

这里主要说下 clear:bothclear:both 意思是说,设置了该属性的元素,其左边和右边不允许存在浮动元素。

  • 父元素高度塌陷的情况:子元素浮动后脱离了文档流,未设置高度的父元素在形式上表现为 0 高度,设置了 clear:both 的元素为了满足其左右两边没有浮动元素的这个条件,只能自身下移,从而带动了父元素高度的撑开。
  • 兄弟元素覆盖的情况:同理,比如A由于浮动覆盖了 C,我们在 AC 之间新增一个 B 元素,则 BC 位于同一文档流,B 为了满足其左右两边没有浮动元素的这个条件,只能自身下移,从而带动了 C 元素向下移动直到没有被 A 覆盖。

那么为什么除了 clear:both 之外,其他方法也能清除浮动呢?因为那些方法大都触发了 BFC,而 BFC 是可以清除浮动的,这个后面再介绍。

2.margin 塌陷

文档流内,块级元素与块级元素在垂直方向上的 margin 有时会合并(塌陷)为单个 margin,这样的现象称之为 margin 塌陷(margin collapse)。具体包括三种情况:

  • 相邻的兄弟元素之间:
    原因:
    相邻的兄弟元素默认位于同一个块级上下文中
    计算规则:
    正正取大值,正负值相加,负负最小值

  • 父元素与第一个/最后一个子元素之间:
    原因:
    a.margin-top 重叠
    父元素非块状格式化上下文元素
    父元素没有border-top设置
    父元素没有padding-top值
    父元素和第一个子元素之间没有inline元素分隔
    b.margin-bottom 重叠
    父元素非块状格式化上下文设置
    父元素没有 border-bottom 设置
    父元素没有 padding-bottom
    父元素和第一个子元素之间没有inline元素分隔
    父元素没有 height,min-height,max-height
    计算规则:
    子元素和父元素上边界重叠,并且以子元素的 margin-top 作为父元素的 margin-top 整体移动。对于 margin-bottom 同理。

  • 空的block元素:
    原因:
    元素没有 border 设置
    元素没有 padding 值
    里面没有 inline 元素
    没有 height 或者min-height

那么怎么解决 margin 塌陷问题呢?同样是利用接下来要讲到的 BFC。

3.BFC

3.1 什么是 BFC?

BFC 即 Block formatting context,译为块级格式化上下文。简单来说,它是一种属性,这种属性影响着元素的定位以及与其兄弟元素之间的相互作用。因为是属性,所以我们通常说“元素具有 BFC”、“元素触发了 BFC”,而不说“元素是 BFC”。

从样式上看,具有 BFC 的元素与普通的容器没有什么区别;但是从功能上,具有 BFC 的元素可以看作是隔离了的独立容器,容器里面的子元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器没有的一些特性。

3.2 如何触发 BFC?

CSS 规定满足下列 CSS 声明之一的元素便会生成 BFC:

  • 根元素或其它包含它的元素;
  • float 的值不为 none;
  • overflow 的值不为 visible;
  • position 的值为 absolute 或 fixed;
  • display 的值为 inline-block、table-cell、table-caption、table、flex、inline-flex、flow-root

table 本身不生成 BFC,而是 table 默认生成的匿名 table-cell 会生成 BFC

3.3 BFC 的特性

从整体上看,BFC 是隔离了的容器,这个具体可以表现为三个特性:

3.3.1 BFC 会阻止 margin 塌陷

前面我们说过 BFC 可以用于解决 margin 塌陷问题。

  • 因为相邻兄弟元素默认位于同一个 BFC 是导致 margin 塌陷的原因,所以我们只需要设法隔离它们两者即可 ———— 假设有兄弟元素 A 和 B,可以使 A 的父元素触发 BFC,此时,触发了 BFC 的父元素里面的 A 子元素不会在布局上影响到 B,也自然不会有 margin 的叠加。
  • 同样地,如果是父子嵌套的 margin 塌陷问题,只需要触发父元素的 BFC 即可。

3.3.2 BFC 可以包含浮动的元素

前面说过,父元素没有设置高度时,子元素的浮动会导致父元素表现为 0 高度,也就是说正常情况下父元素无法包含浮动的子元素。如图:

.Fa{
    border: 2px solid red;
    width: 300px;
}
.son{
    width:180px;
    height:180px;
    background-color:yellow;
    float:left;
}

但是触发了父元素的 BFC 后,父元素将可以包含浮动的子元素。如图:

.Fa{
    border: 2px solid red;
    width: 300px;
    overflow:hidden;  /* 随便一个 BFC 的触发条件 */
}
.son{
    width:180px;
    height:180px;
    background-color:yellow;
    float:left;
}

3.3.3 BFC 可以阻止元素被浮动元素覆盖

前面说过浮动元素会影响兄弟元素的位置,具体地说就是浮动之后脱离了文档流,使得兄弟元素上移填补空缺,而这会使得它被浮动元素覆盖。如图:

但是触发了兄弟元素的 BFC 后,它将不会被浮动的元素覆盖 —— 不会被覆盖,意味着兄弟元素出现在浮动元素的旁边或者下面,具体取决于父元素的宽度。
如果父元素的宽度足以包含这两个子元素的宽度之和,则子兄弟元素和子浮动元素并排。如图(这实际上就是两列布局的一种实现方式):

如果父元素的宽度不足以包含这两个子元素的宽度之和,则子兄弟元素会出现在子浮动元素的下面。如图:

4.可视化格式模型

让我们进一步拓展一下可视化格式模型的一些概念,由于比较复杂,所以只做了解即可。

可视化格式模型规定了客户端(浏览器)如何在媒介(显示器)中渲染文档树(document tree)。如下图,每个节点、元素都有属于自己的可见或不可见、有名或匿名的盒模型,可视化格式模型即规定了这些盒、框框如何整齐地排列在页面中,还有盒子之间的相互作用。

可视化格式模型的体系包括:
1.包含块 CB(Containing block)
2.控制框:块框、行框
3.FC(Formatting Context, 格式化上下文)

  • BFC(Block Formatting Context, 块级格式化上下文)
  • IFC(Inline Formatting Context, 行内格式上下文)
  • GFC(Grid Formatting Context, 网格布局格式化上下文)
  • FFC(Flex formatting contexts, 自适应格式上下文)
    4.定位体系/方案(普通流、定位流、浮动流)
    5.浮动体系

这里只说 FC 和定位体系。

4.1 FC

FC 即 Formatting context,译为格式化上下文。这是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系、相互作用。除了最常见的 CSS2.1 中的 BFC 和 IFC 之外,CSS3 中还增加了 GFC 和 FFC。

4.1.1 BFC:

前面已经说过了。

4.1.2 IFC:

  • IFC 的 line box 高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的 padding/margin 影响)。IFC 的line box 一般左右都贴紧整个 IFC,但是会因为 float 元素而扰乱。float 元素会位于 IFC 与 line box 之间,使得 line box 宽度缩短。 同个 IFC 下的多个 line box 高度会不同。
  • IFC 中是不可能有块级元素的,当插入块级元素时(如 p 中插入 div)会产生两个匿名块与 div 分隔开,即产生两个 IFC,每个 IFC 对外表现为块级元素,与 div 垂直排列。
  • 那么IFC一般有什么用呢?
    水平居中:当一个块要在环境中水平居中时,设置其为 inline-block 则会在外层产生 IFC,通过 text-align 则可以使其水平居中。
    垂直居中:创建一个 IFC,用其中一个元素撑开父元素的高度,然后设置其 vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。

4.1.3 GFC:

  • 当为一个元素设置 display:grid 的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。
  • 那么 GFC 有什么用呢,和 table 又有什么区别呢?首先同样是一个二维的表格,但 GridLayout 会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。

4.1.4 FFC:

display:flex 或者 display:inline-flex 的元素将会生成自适应容器(flex container),可惜这个牛逼的属性只有谷歌和火狐支持,不过在移动端也足够了,至少 safari 和 chrome 还是 OK 的,毕竟这俩在移动端才是王道。
Flex box 由伸缩容器和伸缩项目组成。通过设置元素display:flex 或者 display:inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素。
伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说,Flex box 定义了伸缩容器内伸缩项目该如何布局。

4.2 定位方案

在定位的时候,浏览器会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。在 CSS2.1 中,有三种定位方案 ———— 普通流、浮动和绝对定位,下面分别对这三种布局简略说明一下:

普通流(Normal flow)

  • 在普通流中,盒一个接着一个排列;
  • 在块级格式化上下文里面,它们竖着排列;
  • 在行内格式化上下文里面,它们横着排列;
  • 通常情况下, position 为 static 或 relative,并且 float 为 none ,因而会触发普通流;
  • position 为 static 时,盒的位置是常规流布局里的位置;
  • position 为 relative 时,盒偏移位置由 top,bottom,left 和 right 定义。即使有偏移,仍然保留原有的位置,其它普通流不能占用这个位置。

浮动 (Floats)

  • 元素脱离普通流,并且影响普通流的布局 ———— 导致普通流环绕在它的周边,除非设置 clear 属性;
  • 盒称为浮动盒(floating boxes);
  • 它位于当前行的开头或末尾;

绝对定位 (Absolute positioning)

  • 元素脱离普通流,并且不影响普通流的布局
  • 它的定位相对于它的包含块,相关CSS属性:top,bottom,left 和 right;
  • 如果元素为 position:absoluteposition:fixed,它是绝对定位元素;
  • 对于 position: absolute,元素将相对最近的一个非 static 定位的父元素进行定位,如果没有则相对于 body;

参考:
学习 BFC (Block Formatting Context)
CSS-可视化格式模型