You Don't Know CSS(二)

1,535 阅读5分钟
原文链接: zhuanlan.zhihu.com

Float positioning scheme

float本来是用于实现文本环绕图片效果,但是其却成为了现代栅格布局的基础。一旦你理解normal flow是如何工作和inline formatting context是如何切割成line box的,float就很好理解了。设置'float'属性可以使得元素使用float positioning scheme。

float可以理解为一个脱离normal flow的块状元素。其并不影响其他block-level盒子,但是其会影响block-level盒子内的line box。

注:面试中我们经常被问到的一个问题是“请解释下什么是脱离文档流,或者哪些属性会使得元素脱离文档流”,这个问题本身存在问题,规范中没有脱离文档流这种说法,只有out of normal flow和in-flow两种说法,而out of normal 简单的来说就是不使用normal flow positioning scheme,根据上节所说只有三种positioning scheme:normal flow,floats,absolution positioning,那么out of flow即使指采用float和absolution positioning的元素。

浮动会使得盒子被移动到当前行的左边或右边。关于浮动最有趣的特性是盒子的内容可以围绕在浮动元素周边(当然也可以通过clear属性阻止这种特性)。内容会流动在左浮动元素的右边或右浮动元素的左边

Floats有如下特性

  • Floats在布局阶段是脱离normal flow的,因此其并不影响块状元素的垂直位置。(这个特性说明当考虑存在float的布局时,其并不会影响block-level元素的布局,我们可以假想float元素不存在,当考虑block-level元素内容的line box布局时再考虑float元素的存在
  • Floats与包含容器的左边缘或右边缘对齐
  • Floats自左向右或自右向左,按照其在文档中标记出现的顺序排列。换句话说,对于右浮动元素,第一个右浮动元素放在包含容器的右边缘,第二个右浮动元素则贴近第一个右浮动元素
  • Floats可以影响当前元素或者后续元素line box内的inline-level内容布局。更准确的说是,浮动会导致当前和后续的line box缩短空间为float提供空间。
  • 因为Floats不在normal中,其不影响父元素的高度。这就是为什么我们需要'clearfix'的原因
  • Floats可以使用clear属性清除

如下例所示

.float {
  float: left;
  height: 500px;
}
.para {
  margin: 0;
}
Text inside a block-level box placed on a line box before the float
Text before the float.
The float
Text after the float.
Text inside a block-level box placed on a line box after the float

渲染结果如下

从上面例子可以看出:

  • 所有的block-level div都是垂直的排列,就像float不存在一样
  • float影响了当前和后续的line box的布局,但是没有影响到第一个div内line box的布局

Floats并不会影响不在同一bfc下的盒子内line box布局。这就是说这些元素要么在float的一侧,要么放在前面float的下面。如下例所示

.float {
  float: left;
  height: 500px;
}
.para {
  margin: 0;
}
.new-context {
  margin: 0;
  overflow: auto;
}
before
The float
new formatting context
foo bar
after

渲染效果如下

我们可以看出

  • new-context建立了新的formatting context
  • float元素并没有影响new-context内的line box。new-context紧邻float的边缘放置
  • float元素仍然影响了后续div的line box。因为它们没有创建新的bfc。
这里我们又看到了bfc的一个应用,如果阻止margin 重叠一样,这里bfc起作用是因为float对line box的影响只限于同一bfc下。

这种特性十分有用,大多数的删格框架都利用了floats和overflow进行布局。

Float clearing

CSS规范允许你通过clear属性来清除浮动,如下例所示

.left, .right {
  width: 35%;
  height: 40px;
}
.left {
  float: left;
}
.right {
  float: right;
}
.clear-left {
  clear: left;
}
.clear-both {
  clear: both;
}
A
B
C
D
Clear left only. Clear left only. Clear left only. Clear left only. Clear left only. Clear left only.
E
F
G
H
Clear both. Clear both. Clear both. Clear both. Clear both. Clear both. Clear both.

渲染效果如下

Floats另一特性是其并不计如父元素的高度计算。如果父元素里除了浮动元素外没有其他元素,那么父元素的高度就为0,如下例所示:

.left {
  float: left;
  width: 35%;
  height: 40px;
}
A
B
C
D

渲染效果如下

原因在于“content-based"的高度计算有两种形式,对于overflow:visible的block-level元素其高度计算并不记入浮动子元素,而对于overflow为其他值的元素或者设置了清除浮动的元素其高度计算记入浮动子元素。关于高度计算更深入的细节在将在盒子模型那一章里讲。

The clearfix

clearfix技巧用于清除浮动。其有几种变种,下面讲一下其机理。

clearfix可以实现如下几种效果

  • 其可以阻止在被清除浮动的父元素里的浮动子元素影响其他元素里的line box
  • 其可以使得被清除浮动的父元素高度计算记入浮动子元素的高度

如下例所示

.left {
  float: left;
  width: 15%;
  height: 40px;
}
A
B
C
D
E
F

渲染效果如下

我们想实现每个row单独成一行有以下三种方法

  • 在父元素的末尾处添加一个元素,其带有属性clear:both
  • 使用伪元素方法在父元素的末尾添加一个带有属性clear:both的元素
  • 使用overflow:hidden或者overflow:auto让父元素新建bfc。

代码如下

伪元素法:

.clearfix:after {
  content: "";
  display: table;
  clear: both;
}
.left {
  float: left;
  width: 15%;
  height: 40px;
}
A
B
C
D
E
F

新建bfc法:

.clearfix {
  overflow: auto;
}
.left {
  float: left;
  width: 15%;
  height: 40px;
}
A
B
C
D
E
F

其中伪元素方法更为常用,因为其避免了元素溢出带来的问题。为一个元素设置overflow不为visible的属性可能使得内容被剪裁,如下所示:

.clearfix-overflow {
  overflow: auto;
}
.clearfix-pseudo:after {
  content: "";
  display: table;
  clear: both;
}
.left {
  float: left;
  width: 15%;
  height: 40px;
}
.offset-1 {
  position: relative;
  top: 15px;
}
.offset-2 {
  position: relative;
  top: 30px;
}
A
B
C

D
E
F

渲染效果如下

我们可以看出使用overflow:auto造成了子元素溢出的部分受到剪裁,这可能不是我们想要的。

而使用clear:both则不会造成这种后果。