css布局--盒&流的前世今生

288 阅读8分钟

本文旨在从css规范发展的角度对css布局这一概念进行具像化介绍,希望能在这个过程中拨开一些迷雾般的概念。

Css1

Css1规范的发布日期是1996/12/17,在此后的时间里有过不同的修订。在这其中提到的关于布局的内容如下:

格式化模型

4.Formatting model

CSS1 assumes a simple box-oriented formatting model where each formatted element results in one or more rectangular boxes.

CSS1假设了一个简单的以盒为导向的格式化模型,其中每个格式化的元素都生成一个或多个矩形盒。

盒模型作为格式化模型的具体实现在css1中被提到。

在这里content和paddingbordermargin构成了这个box。

块和内联

From the formatter's point of view there are two main types of elements: block-level and inline.

从格式化器的角度来看,有两种主要的元素类型:块级和内联

块级(Block-level)元素

Elements with a 'display' value of 'block' or 'list-item' are block-level elements. Also, floating elements (elements with a 'float' value other than 'none') are considered to be block-level elements.

displayblocklist-item的元素是块级元素;浮动元素(除了float:none)被认为是块级元素

垂直格式化

非浮动的块级元素在垂直方向上的格式化有以下规则:

  • 相邻块级元素之间会有边距折叠(margin-bottommargin-top取较大值进行绘制)
  • margin-topmargin-bottom为负值时,从相邻margin的最大值中扣除
浮动元素

Using the 'float' property, an element can be declared to be outside the normal flow of elements and is then formatted as a block-level element.

使用float属性,一个元素可以被声明在元素的normal flow(此时应该还没有特定的常规流概念,整个文档也只有4.1.4使用了normal flow字眼)之外,然后格式化为块级元素

内联(Inline)元素

Elements that are not formatted as block-level elements are inline elements.

没有被格式化为块级元素的元素时内联元素

Css2.1

Css2在1998年发布,但由于存在的一些问题,导致了后来更广为应用的css2.1规范的诞生,css2.1在2004年发布,是对css2的一次重大修订,以至于后来实际谈论和使用的css2实际上是css2.1。

css2.1的内容比css1更为丰富,其中关于布局中的盒模型、格式化概念有了更多的章节,接下来将从这些章节延续css1中的探索。

盒模型Box Model

在css2.1的第8章中明确提出了盒模型的概念,其中的内容更像是对css1中格式化模型的丰富,详细介绍了content、marginpaddingborder相关的内容,在这里不过多赘述。

视觉格式化模型Visual formatting model

visual formatting model: how user agents process document tree for visual media.

视觉格式化模型:对于可视化媒体,浏览器如何处理文档树

In the visual formatting model, each element in the document tree generates zero or more boxes according to the box model.

在视觉格式化模型中,文档树中的每个元素根据盒模型生成一个或多个盒子

在这里盒成为了视觉格式化的基本单位。对于同级或父级它们作为盒存在,对于子级:盒是它子级的包含块

盒类型display

A box's type affects, in part, its behavior in the visual formatting model. The 'display' property, described below, specifies a box's type.

盒子的类型在一定程度上影响它在视觉格式化模型中的表现。盒子的类型通过属性'display'声明。

块级元素

display属性的值为:blocklist-itemtable时,元素将变成块级的。

每个块级元素生成一个主块级盒(principal block-level box),用来包含后代盒和生成的内容,任何定位方案都与该盒有关。有些块级元素('list-item')会生成除主盒外的额外的盒,额外盒根据主盒来放置。

块级盒

块级盒是参与块级格式化上下文(block formatting context,bfc)的盒。它独占一行,其宽度默认是其父元素的宽度,高度由内容决定,但可以通过 widthheight 属性来设置。

行内级元素

Inline-level elements are those elements of the source document that do not form new blocks of content

行级元素就是那些在源文档中没有生成块级内容的元素

display属性的值为:inlineinline-tableinline-block,元素将被声明为行内级盒

行内级盒Inline-level box

行内级盒是参与行格式化上下文(inline formatting context)的盒,根据display的值,它有以下两种:

  • inline,生成inline box,行内盒是(特殊的)行内级盒,它的内容参与了它所在行的格式化上下文,行内盒子的宽度由它们的内容决定,而不是由 width 属性决定

    <span class=container>
      白裙红衣的姑娘桥上婀娜,这一方风土名曰摩梭
    </span>
    
    .container {
      width: 100px;
    }
    

    此时display: inline 属性可防止 width 产生影响。

  • inline-tableinline-block,生成Atomic inline-level box,不属于行内盒的行内级盒被称为原子行内级盒,它作为单一的不透明盒参与它所在行的格式化上下文

关于行内盒和原子行内级盒的区别,可以看下面的代码:

<!DOCTYPE html>
<html>
<head>
    <style>
        .container {
            width: 500px;
        }
    </style>
</head>
<body>
    <div class="container">
        <span style="border: 1px solid red;">这是一个行内盒,它的内容是这段文本。</span>
        <div style="display: inline-block; border: 1px solid blue;">这是一个原子行内级盒,它的内容是这段文本。</div>
    </div>
</body>
</html>

通过调换两个行内元素的先后顺序,可以看到这两种盒子的区别。

替换元素和非替换元素

定位方案

css2.1中,一个box会根据3种定位方案(position schemes)来确定位置:

  • Normal flow,常规流(这里是明确提出概念),常规流包括块级盒的块格式化(block formatting of block-level boxes),行内级盒的行内格式化(inline formatting of inline-level boxes),块级与行内盒的相对定位(relative position of block-level and inline-level boxes)。
  • Floats,浮动,在浮动模型中,盒先根据常规流来放置,然后从常规流取出,并尽可能地向左或向右移动。其他内容沿着浮动盒一侧排列。
  • Absolute position,绝对定位。一个盒会从常规流中全部移除,并根据包含块(父级盒)确定位置。

定位属性popsition

定位元素

如果元素的position属性有一个除static外的值,就说它是定位元素。定位的元素生成定位的盒(position boxes

属性值:

  • static,常规盒,根据常规流布局。top,right,bttomleft将失效
  • relative,盒的位置根据其常规流布局位置偏移
  • absolute,盒的位置&大小,由top,right,bttomleft指定。绝对定位的盒脱离了常规流,不会影响其他兄弟元素的布局。虽然有外边距,但不会和其他外边距合并
  • fixed,盒的位置参照的是viewport

配套属性——盒偏移:

  • top: 对于position:absolute指定了到定位包含块(具有static除外的position属性的祖先元素)上边界的距离;对于position:fixed指定了到viewport上边界的距离;对于position:relative指定了参照自身在常规流中的上边界进行偏移。
  • 对于rightleftbottom有相似的作用

常规流Normal flow

Boxed in the normal flow belong to a formatting context, which maybe block or inline, but not both simultaneously.

常规流中的盒子属于一个格式化上下文,可能是块或行内(格式化上下文),但不能两者都是。

块格式化上下文(Block formatting context,BFC)

floatsabsolutely positioned elementsblock containers that are not block boxes(such as inline-blocks, table-cells, table-captions)、block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting context for their contents

浮动、绝对定位元素、非块盒的块容器(如inline-blocks, table-cells, table-captions)、overflow不为visible的块元素(除非该值已经被传递到viewport,如元素)(overflow:visible是默认的属性,这解释了为什么div会因其中的浮动元素塌陷,需要手动开启BFC)

BFC有以下规则:

  • 在一个BFC中,box在垂直方向上一个接一个地放置;
  • 两个兄弟box之间的垂直距离由margin决定;
  • 同一个BFC中,相邻block-level box的垂直外边距(margin-topmargin-bottom)会合并(css1中提到的边距折叠)
行内格式化上下文(Inline formatting context,IFC)

In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes. The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.

在一个行内格式化上下文里,从块盒子的顶部开始在水平方向上一个接一个地放置,水平方向上的marginborderpadding在这些盒子间都是有效的。盒子在垂直方向上以不同的方式对齐:以他们的顶部或底部对齐,或者以它们中的文本的baseline对齐。包含形成一行的盒子的矩形区域叫做行盒子line box

在这里提出了一个新的概念"line box",行盒子是描述块级元素内部的行内内容布局的概念。

行盒子有许多有趣的属性:

行盒子总是足够高,容纳它包含的所有盒。

line-height:

非替换元素的高度受到line-height的影响。

// 行内文字垂直居中
<style>
.ifc {
	height: 100px;
  line-height: 100px;
}
</style>
<body>
  <div class="ifc">
    <span>文字垂直居中</span>
  </div>
</body>

vertical-align:

line box 中的元素垂直对齐方式

// 行内图片居中水平对齐文字
<style>
.ifc {
  height: 100px;
  line-height: 100px;
}
 img {
 	height: 50px;
  width: 50px;
  vertical-align: middle;
}
</style>
<body>
  <div class='ifc'>
    hello,hello<img src="">
  </div>
</body>