乐趣区

BFC,包含块,文档流,浮动,定位是个啥关系—CSS视觉格式化模型

CSS 视觉格式化模型
浏览器在解析渲染我们所写的 html 内容,顺序渲染(普通文档流)。
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</body>
渲染结果如下图
这儿有一个知识点:1、文档流:按我的理解就是 html 在浏览器渲染显示的一个模式,这个模式的特点:自上而下,从左到右(排列规则)。如果不特殊指定,浏览器会默认当前的 HTML 渲染是按文档流模式。2、浮动:平时我们所知的脱离文档流的一种方式,把 html 元素的布局更改,浮动在视图层上 3、绝对定位:脱离文档流的另外一种方式,也是渲染在视图层上
格式化模型是什么呢?
这儿又有个知识点:css 盒模型
盒模型是 css 渲染的时候抽象于 html 标签在画布所占有区域,由 CSS 引擎根据文档中的内容所创建。
盒模型的关键属性:内容 (content)、内边距 (padding)、边框 (border)、外边距 (margin)。
盒模型是有两种标准的,一个是标准模型,一个是 IE 模型。
从图中很直观能看出来,两种模型的宽度和高度的计算规则不一样。
在 css 中,两个模型的定义方式为:
/* 标准模型 */
box-sizing:content-box;

/*IE 模型 */
box-sizing:border-box;
视觉格式化模型会根据 CSS 盒子模型将文档中的元素转换为一个个盒子。
每个盒子的布局由以下因素决定:
盒子的尺寸:精确指定、由约束条件指定或没有指定
盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块盒子(block)
定位方案(positioning scheme):普通流定位、浮动定位或绝对定位
文档树中的其它元素:即当前盒子的子元素或兄弟元素
视口尺寸与位置
所包含的图片的尺寸
其他的某些外部因素
该模型会根据盒子的包含块(containing block)的边界来渲染盒子。通常,盒子会创建一个包含其后代元素的包含块,但是盒子并不由包含块所限制,当盒子的布局跑到包含块的外面时称为溢出(overflow)
在这儿把我们用到的术语做一个解释说明:
块:block,一个抽象的概念,一个块在文档流上占据一个独立的区域,块与块之间在垂直方向上按照顺序依次堆叠。
包含块:containing block,包含其他盒子的块称为包含块。
盒子:box,一个抽象的概念,由 CSS 引擎根据文档中的内容所创建,主要用于文档元素的定位、布局和格式化等用途。盒子与元素并不是一一对应的,有时多个元素会合并生成一个盒子,有时一个元素会生成多个盒子(如匿名盒子)。
块级元素:block-level element,元素的 display 为 block、list-item、table 时,该元素将成为块级元素。元素是否是块级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
块级盒子:block-level box,由块级元素生成。一个块级元素至少会生成一个块级盒子,但也有可能生成多个(例如列表项元素)。我们查询资料会看到:块格式化上下文(Block Formatting Context,BFC),就是当前的区域。
块盒子:block box,如果一个块级盒子同时也是一个块容器盒子(见下),则称其为块盒子。除具名块盒子之外,还有一类块盒子是匿名的,称为匿名块盒子(Anonymous block box),匿名盒子无法被 CSS 选择符选中。
块容器盒子:block container box 或 block containing box,块容器盒子侧重于当前盒子作为“容器”的这一角色,它不参与当前块的布局和定位,它所描述的仅仅是当前盒子与其后代之间的关系。换句话说,块容器盒子主要用于确定其子元素的定位、布局等。

注意:盒子分为“块盒子”和“块级盒子”两种,但元素只有“块级元素”,而没有“块元素”。下面的“行内级元素”也是一样。

行内级元素:inline-level element,display 为 inline、inline-block、inline-table 的元素称为行内级元素。与块级元素一样,元素是否是行内级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
行内级盒子:inline-level box,由行内级元素生成。行内级盒子包括行内盒子和原子行内级盒子两种,区别在于该盒子是否参与行内格式化上下文的创建。行内格式化上下文(inline formatting context,IFC),就是当前区域。
行内盒子:inline box,参与行内格式化上下文创建的行内级盒子称为行内盒子。与块盒子类似,行内盒子也分为具名行内盒子和匿名行内盒子(anonymous inline box)两种。
原子行内级盒子:atomic inline-level box,不参与行内格式化上下文创建的行内级盒子。原子行内级盒子一开始叫做原子行内盒子(atomic inline box),后被修正。原子行内级盒子的内容不会拆分成多行显示。

块级元素与块盒子
当元素的 display 为 block、list-item 或 table 时,该元素将成为块级元素。一个块级元素会被格式化成一个块(例如文章的一个段落),默认按照垂直方向依次排列。
比较生硬的知识 BFC,IFC,后面的说明中会用到:
BFC
创建 BFC 区域的方式:
根元素或包含根元素的元素
浮动元素(元素的 float 不是 none)
绝对定位元素(元素的 position 为 absolute 或 fixed)
行内块元素(元素的 display 为 inline-block)
表格单元格(元素的 display 为 table-cell,HTML 表格单元格默认为该值)
表格标题(元素的 display 为 table-caption,HTML 表格标题默认为该值)
匿名表格单元格元素(元素的 display 为 table、table-row、table-row-group、table-header-group、table-footer-group(分别是 HTML table、row、tbody、thead、tfoot 的默认属性)或 inline-table)
overflow 值不为 visible 的块元素
display 值为 flow-root 的元素
contain 值为 layout、content 或 strict 的元素
弹性元素(display 为 flex 或 inline-flex 元素的直接子元素)
网格元素(display 为 grid 或 inline-grid 元素的直接子元素)
多列容器(元素的 column-count 或 column-width 不为 auto,包括 column-count 为 1)
column-span 为 all 的元素始终会创建一个新的 BFC,即使该元素没有包裹在一个多列容器中

BFC 对浮动定位与清除浮动都很重要。浮动定位和清除浮动时只会应用于同一个 BFC 内的元素。浮动不会影响其它 BFC 中元素的布局,而清除浮动只能清除同一 BFC 中在它前面的元素的浮动。外边距折叠也只会发生在属于同一 BFC 的块级元素之间
BFC 区域内模型的规则:
1、内部的 box 会在垂直方向,一个接一个的放置
2、每个元素的 margin box 的左边,与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)
3、box 垂直方向的距离由 margin 决定,属于同一个 BFC 的两个相邻 box 的 margin 会发生重叠
4、BFC 的区域不会与浮动区域的 box 重叠
5、BFC 是一个页面上的独立的容器,外面的元素不会影响 BFC 里的元素,反过来,里面的也不会影响外面的
6、计算 BFC 高度的时候,浮动元素也会参与计算

来看一个例子,margin 重叠的问题:
<!DOCTYPE html>
<html lang=”en”>

<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}

.top{
background: #0ff;
height:100px;
margin-bottom:30px;
}
.bottom{
height:100px;
margin-top:50px;
background: #ddd;
}
</style>
</head>

<body>
<div class=”top”>
<h1> 上 </h1>
margin-bottom:30px;
</div>
<div class=”bottom”>
<h1> 下 </h1>
margin-top:50px;
</div>
</body>

</html>
上述视图效果,上下边距重叠取最大的值。
用 BFC 可以解决垂直 margin 重叠的问题
<section class=”top”>
<h1> 上 </h1>
margin-bottom:30px;
</section>

<!– 给下面这个块添加一个父元素,在父元素上创建 BFC –>
<div style=”overflow:hidden”>
<section class=”bottom”>
<h1> 下 </h1>
margin-top:50px;
</section>
</div>

每个块级盒子都会参与块格式化上下文(block formatting context)的创建,而每个块级元素都会至少生成一个块级盒子,即主块级盒子(principal block-level box)。有一些元素,比如列表项会生成额外的盒子来放置项目符号,而那些会生成列表项的元素可能会生成更多的盒子。不过,多数元素只生成一个主块级盒子。
主块级盒子包含由后代元素生成的盒子以及内容,同时它也会参与定位方案。
定位规则:一旦生成了盒子以后,CSS 引擎就需要定位它们以完成布局。下面是定位盒子时所使用的规则:
普通流:按照次序依次定位每个盒子
浮动:将盒子从普通流中单独拎出来,将其放到外层盒子的某一边
绝对定位:按照绝对位置来定位盒子,其位置根据盒子的包含元素所建立的绝对坐标系来计算,因此绝对定位元素有可能会覆盖其他元素

一个块级盒子可能也是一个块容器盒子。块容器盒子(block container box)要么只包含其它块级盒子,要么只包含行内盒子并同时创建一个行内格式化上下文(inline formatting context)。
能够注意到块级盒子与块容器盒子是不同的这一点很重要。前者描述了元素与其父元素和兄弟元素之间的行为,而后者描述了元素跟其后代之间的行为。有些块级盒子并不是块容器盒子,比如表格;而有些块容器盒子也不是块级盒子,比如非替换行内块和非替换表格单元格。
一个同时是块容器盒子的块级盒子称为块盒子(block box)。
匿名块盒子
在某些情况下进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能用 CSS 选择符选中,因此称为匿名盒子(anonymous boxes)。
CSS 选择器不能作用于匿名盒子 (anonymous boxes),所以它不能被样式表赋予样式。也就是说,此时所有可继承的 CSS 属性值都为 inherit,而所有不可继承的 CSS 属性值都为 initial。
块包含盒子可能只包含行内级盒子,也可能只包含块级盒子,但通常的文档都会同时包含两者,在这种情况下,就会在相邻的行内级盒子外创建匿名块盒子。
假如 div 和 p 标签都保持默认样式(即它们的 display 为 block):
<div>Some inline text <p>followed by a paragraph</p> followed by more inline text.</div>
此时会产生两个匿名块盒子:一个是 <p> 元素前面的那些文本(Some inline text),另一个是 <p> 元素后面的文本(followed by more inline text.)。此时会生成下面的块结构:
对这两个匿名盒子来说,程序员无法像 <p> 元素那样控制它们的样式,因此它们会从 <div> 那里继承那些可继承的属性,如 color。其他不可继承的属性则会设置为 initial,比如,因为没有为它们指定 background-color,因此其具有默认的透明背景,而 <p> 元素的盒子则能够用 CSS 指定背景颜色。类似地,两个匿名盒子的文本颜色总是一样的。
另一种会创建匿名块盒子的情况是一个行内盒子中包含一或多个块盒子。此时,包含块盒子的盒子会拆分为两个行内盒子,分别位于块盒子的前面和后面。块盒子前面的所有行内盒子会被一个匿名块盒子包裹,块盒子后面的行内盒子也是一样。因此,块盒子将成为这两个匿名块盒子的兄弟盒子。
如果有多个块盒子,而它们中间又没有行内元素,则会在这些盒子的前面和后面创建两个匿名块盒子:
假设 <p> 的 display 为 inline,<span> 的 display 为 block:
<p>Some <em>inline</em> text <span>followed by a paragraph</span> followed by more inline text.</p>
此时会产生两个匿名块盒子:一个是 <span> 元素前面的文本(Some inline text),另一个是其之后的文本(followed by more inline text.)。此时会生成下面的块结构:
行内级元素和行内盒子
如果一个元素的 display 属性为 inline、inline-block 或 inline-table,则称该元素为行内级元素。显示时,它不会生成内容块,但是可以与其他行内级内容一起显示为多行。一个典型的例子是包含多种格式内容(如强调文本、图片等)的段落,就可以由行内级元素组成。
行内级元素会生成行内级盒子,该盒子同时会参与行内格式化上下文(inline formatting context)的创建。行内盒子既是行内级盒子,也是一个其内容会参与创建其容器的行内格式化上下文的盒子,比如所有具有 display:inline 样式的非替换盒子。如果一个行内级盒子的内容不参与行内格式化上下文的创建,则称其为原子行内级盒子。而通过替换行内级元素或 display 值为 inline-block 或 inline-table 的元素创建的盒子不会像行内盒子一样可以被拆分为多个盒子。
在同一个行内格式化上下文中,原子行内级盒子不能拆分成多行:
<style>
span {
display:inline; /* default value*/
}
</style>
<div style=”width:20em;”>
The text in the span <span>can be split in several
lines as it</span> is an inline box.
</div>
显示为:The text in the span can be split into severallines as it is an inline box.
<style>
span {
display:inline-block;
}
</style>
<div style=”width:20em;”>
The text in the span <span>cannot be split in several
lines as it</span> is an inline-block box.
</div>
显示为:The text in the span cannot be split into several lines as it is aninline-block box.
其中的“cannot be split into several lines as it”永远不会换行。
IFC
行内格式化上下文(inline formatting context)
行框 (line boxes)
相对于 BFC,在 IFC 中,框(boxes)一个接一个地水平排列,起点是包含块的顶部。水平方向上的 margin,border 和 padding 在框之间得到保留。框在垂直方向上可以以不同的方式对齐:它们的顶部或底部对齐,或根据其中文字 的基线对齐。包含那些框的长方形区域,会形成一行,叫做行框。
行框的宽度有它的包含块和其中的浮动元素决定。高度的确定由行高度计算规则决定。
行框的范围
通常,行框的左边接触到包含块的左边,右边接触到其包含块的右边。然而,浮动元素可能会处于包含块边缘和行框边缘之间。总之,尽管在相同的行内格式化上下文中的行框通常拥有相同的宽度(包含块的宽度),它们可能会因浮动元素缩短了可用宽度,而在宽度上发生变化。同一行内格式化上下文中行框通常高度不一样(如,一行包含了一个高的图形,而其它行只包含文本)。
例子:
<!DOCTYPE html>
<html lang=”en”>

<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}

.p-box {
background: rgb(87, 97, 97);
/* width: 100px; */
overflow: hidden;
}

.span-float {
border: 2px solid rgb(255, 163, 163);
font-size: 50px;
float: left;
}
.inline {
border: 2px solid rgb(102, 216, 197);
font-size: 30px;
}
.primary {
border: 2px solid rgb(248, 109, 207);
}
</style>
</head>

<body>
<p class=”p-box”>
<span class=”span-float”>float</span>
<em class=”inline”>inline box</em>
<span class=”primary”>primary</span>
</p>
</body>

</html>
显示结果:
行内框可能被分割
如果几个行内框在水平方向无法放入一个行框内,它们可以分配在两个或多个垂直堆叠的行框中。因此,一个段落就是行框在垂直方向上的队叠。行框在堆叠时没有垂直方向上的分割且永不重叠。
如果一个行内框超出包含它的行框的宽度,它会被分割成几个框,并且这些框会被分布到几个行框内。如果一个行框不能被分割(例如:行内框只包含单个字符,或者语言特殊的断字规则不允许在行内框里换行,或者行内框受到带有“nowrap“或”pre“值的”white-space“特性的影响),这时,行内框会益处行框。
如果一个行内框被分割,margin,padding 和 border 在所有分割处没有视觉效果。
行内框还可能由于双向文本处理(bidirectional text processing)而在同一个行框内被分割为好几个框。具体看上面的例子。
行内框的对齐
1)、行内框在行框中垂直方向上的对齐
行框的高度总是足够容纳所包含的所有框。不过,它可能高于它包含的最高的框(例如:框对齐会引起基线对齐)。当一个框 B 的高度小雨包含它的行框的高度时,B 在行框中垂直方向上的对齐决定于”vertical-aligin“的特性。”vertical-align“默认值基线(baseline)对齐。
2)、行内框在行框中水平方向上的对齐
当一行中行内宽度的总和小于包含它们的行框的宽,它们在水平方向上的对齐,取决于 ”text-align” 特性。如果其值是”justify“,用户端也可以拉伸行内框(除了”inline-table“和”inline-block“框)中的空间和文字。
空的行内框应该被忽略
不包含文本,保留空白符,margin/padding/border 非 0 的行内元素,以及其他常规流中的内容(比如,图片,inline-blocks 和 inline-tables),并且不是以换行结束的行框,必须被当作 0 高度行框对待。就外边距折叠而言,这种行框必须被忽略。
匿名行内盒子
类似于块盒子,CSS 引擎有时候也会自动创建一些行内盒子。这些行内盒子无法被选择符选中,因此是匿名的,它们从父元素那里继承那些可继承的属性,其他属性保持默认值 initial。
一种常见的情况是 CSS 引擎会自动为直接包含在块盒子中的文本创建一个行内格式化上下文,在这种情况下,这些文本会被一个足够大的匿名行内盒子所包含。但是如果仅包含空格则有可能不会生成匿名行内盒子,因为空格有可能会由于 white-space 的设置而被移除,从而导致最终的实际内容为空。
浮动
在浮动定位中,浮动盒子会浮动到当前行的开始或尾部位置。这会导致普通流中的文本及其他内容会“流”到浮动盒子的边缘处,除非元素通过 clear 清除了前面的浮动。
一个盒子的 float 值不为 none,并且其 position 为 static 或 relative 时,该盒子为浮动定位。如果将 float 设置为 left,浮动盒子会定位到当前行盒子的开始位置(左侧),如果设置为 right,浮动盒子会定位到当前行盒子的尾部位置(右侧)。不管是左浮动还是右浮动,行盒子都会伸缩以适应浮动盒子的大小。
绝对定位
在绝对定位中,盒子会完全从当前流中移除,并且不会再与其有任何联系(译注:此处仅指定位和位置计算,而绝对定位的元素在文档树中仍然与其他元素有父子或兄弟等关系),其位置会使用 top、bottom、left 和 right 相对其包含块进行计算。
如果元素的 position 为 absolute 或 fixed,该元素为绝对定位。
对固定位置的元素来说,其包含块为整个视口,该元素相对视口进行绝对定位,因此滚动时元素的位置并不会改变。
GFC
GFC(GridLayout Formatting Contexts) 意为“网格布局格式化上下文”当一个元素设置为 display:grid 的时候,此元素将获得一个独立的渲染区域,可以在网格容器上定义网格行和列,为每一个网格定义位置和空间。GFC 和 table 的区别在于 GridLayout 会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染。
FFC
FFC(Flex Formatting Contexts) 意为“自适应格式化上下文”,display 值为 flex 或者 inline-flex 的元素将会生成自适应容器,当前只有谷歌和火狐支持该属性,不过在移动端几乎没有障碍 (移动端是 chrome 和 Safari 的天下)。flex box 由伸缩容器和伸缩子元素组成。通过设置元素 display:flex/inline-flex 可以得到伸缩容器,前者为块级元素,后者为行内元素。伸缩容器外元素不受影响。

退出移动版