Skip to content

包含块

参考

一个元素的尺寸(如百分比尺寸单位)和位置(如position)经常受其包含块的影响。

查找包含块

如何确认元素的包含块呢?参考下图

  • 如果 position 属性为 static 或 relative ,包含块就是由它的最近的祖先块元素或格式化上下文的内容区的边缘组成的
  • 如果 position 属性为 absolute ,包含块就是由它的最近的 position 的值不是 static 的祖先元素的内边距区的边缘组成。
  • 如果 position 属性是 fixed,包含块是 viewport或分页区域
  • 如果 position 属性是 absolute 或 fixed,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的
    • transform或perspective取值不为none
    • will-change取值为transform
    • filter 取值不为 none 或will-change 取值为filter (仅fire-fox)
    • contain取值为paint

尺寸计算

  • height top 及 bottom 中的百分值,通过包含块的height值计算
    • 如果包含块高度随内容变化,且包含块position为relativestatic,则上述属性取值为0
  • width, left, right, padding, margin 中的百分比,通过包含块的width值计算
    • 注意paddingmargin四个方向上的百分比去值都按照width计算,基于此可以实现一个响应式的正方形

上面谈到的包含块widthheight都是指其盒子模型的完整宽度,即对于content-box而言,包含width + border + padding

定位

参考之前的整理

不同类型的定位会影响对应元素框的生成:

  • static,块级元素生成矩形元素框,并作为文档流的一部分,内联元素生成行内框,置于其父元素中;
  • relative,元素偏移某个距离,其原本所占据的文档流位置仍然保留,其包含块由最近的块级框,表格单元格或者行内块祖先框的内容边界构成,
  • absolute,元素框从文档流中完全删除,并相当于其包含块定位,其包含块由最近的 position 属性值不为 static 的祖先元素的内容边界构成
  • fixed,根据浏览器视窗确定位置

不同的定位形式会影响topbottomleftright等属性的计算。

这里需要了解元素定位时过分受限(margin:auto)的情形,浏览器会忽略对应方向上的偏移并重新计算其偏移,甚至是竖直范行方向上的外边距,因此可以使用 margin: auto 0 达到竖直居中

BFC

参考:

格式上下文

盒子模型,是CSS布局的对象和基本单位,元素的类型与display的属性,决定了这个盒子的类型。不同的盒子,其渲染方式(Formatting Context)是不一样的。常见的格式上下文有

  • BFC(Block Formatting Contexts)直译为"块级格式化上下文"。Block Formatting Contexts就是页面上的一个隔离的渲染区域,容器里面的子元素不会在布局上影响到外面的元素,反之也是如此。
  • IFC(Inline Formatting Contexts)直译为"内联格式化上下文",IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)
  • FFC(Flex Formatting Contexts)直译为"自适应格式化上下文",display值为flex或者inline-flex的元素将会生成自适应容器(flex container)
  • GFC(GridLayout Formatting Contexts)直译为"网格布局格式化上下文",当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域

对于开发者而言,BFC是最常接触的一种渲染方式,常见的外边距折叠、清浮动等问题都可以归根到BFC处理。

触发场景

满足下面任意一个条件的块级元素都会触发BFC

  • 根元素,是指文档树中没有父元素的元素,也就是最顶层结构的元素,一般情况下是html元素;
  • float属性不为none,即float为left||right;
  • position不为relative,即position为absolute||fixed;
  • display为inline-block, table-cell, table-caption, flex, inline-flex,这里特别需要注意的是inline-block。
  • overflow不为visible,即overflow为hidden||auto||scroll,这里特别需要注意的是hidden;

布局规则

  • 内部的Box会在垂直方向,一个接一个地放置。
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠,准确的说只有在同一个BFC内的元素才会发生外边距折叠。
  • 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此,这就是BFC所具有的大名鼎鼎的独立区域属性,躲进小楼成一统,管它春夏与秋冬。
  • BFC的区域不会与float box重叠,即旁边的浮动元素是无法遮挡住BFC元素的。
  • BFC可以包含浮动元素,意思就是BFC的高度计算是包括其浮动的子元素的,浮动元素的高度也参与BFC高度计算(重要的事情说两遍!),这下即使子元素全部都是浮动元素,BFC也可以知道它自己的高度了。

外边距折叠

collapsing-margin,两个或多个毗邻的普通流中的块元素垂直方向上的 margin 会重叠

上面这句定义可以理解为

  • 只有竖直方向上的外边距才会发生重叠。
  • 最少存在两个块级元素才会发生外边距重叠。注意这里其实有两种情况的重叠:两个兄弟块元素之间的重叠,以及子块元素与父级元素之间的重叠。
  • 只有处于普通流中,且相邻的块才会发生重叠。注意,这里的相邻:如果是兄弟元素,指的是他们之间没有被非空内容,或者带有padding,border和clear等在文档流中占有面积的块分隔开来;如果是父子元素,指的是子块带有margin,父块没有padding和border的情况,此时如果父块没有触发BFC,则父块与子块的也会发生外边距折叠。(最常见的情形就是并没有为父块设置外边距时,整个父块却活生生地向下偏移了N个像素,而此时子块紧贴着父块的顶部,其本来具有的外边距却不见了的诡异事件,就像是外边距“渗透“了一样)
  • 这句话并没有表现出来,这是由BFC规定的。只有属于同一个BFC的两个块元素才会发生外边距重叠。

当两个元素发生外边距重叠时,这两个元素之间的距离按照下面标准取舍:

  • 全部都为正值,取最大者;
  • 不全是正值,则都取绝对值,然后用正值减去最大绝对值;
  • 全部都是负值,则都取绝对值,然后用0减去最大绝对值。

浮动与清浮动

float CSS属性指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它。该元素从网页的正常流动(文档流)中移除,尽管仍然保持部分的流动性(与绝对定位相反)。

参考

浮动

学习浮动,首先需要明白文档流的概念:文档流是默认的网页布局模式,是文档中的对象在排列时所占用的位置。在文档流中,块状元素从上到下排列;内联元素(及内联-块状元素)从左到右排列。 对于块状元素而言,如果不设置其宽度,在默认情况下(非浮动、绝对定位等),会在水平方向上自动填满外部的容器;如果设置了宽度,块状元素仍然以行的形式占据位置。也就是说不论如何,普通的div都是独占一行的。

不管多么复杂的布局,其最终目标是都为了在同一行排列多个div元素,因此,鉴于普通块级元素独自霸占一行的做法,我们就需要对它做一点事,使之变得“不再普通”,也就是常说的使块级元素脱离普通文档流,从而达到布局的目的。使元素脱离文档流一般有浮动和定位两种做法,这里主要介绍浮动。

元素(不论是块级元素还是内联元素),加了浮动属性之后会脱离文档流,并按照指定的方向移动,碰到其父容器边界或者是另外一个浮动元素才停下来,浮动元素的影响可以分成三部分:

  • 对于自身的影响;
  • 对于其他的同级非浮动元素的影响;
  • 对于其他的同级浮动元素的影响。

浮动元素对自身的影响

浮动的元素会触发形成BFC,具有BFC的一些布局特性。

  • 由于块级元素的流动特性可知,在不设置宽度的默认情况下其宽度会尽可能铺满一整行。而当块级元素浮动之后其宽度由预先指定的宽度或其内容决定。
  • 浮动的元素都会变成类似于块级元素的状态,也就是说可以为一个浮动的span设置宽高(但是并不会在其style中显示display:block)。
  • 浮动元素在竖直方向上会尽可能的向父容器上方壁靠拢,当然具体的位置需要参考“对于其他的同级浮动元素的影响”这一点。
  • 浮动元素的"pading""border""margin"属性仍然存在,这是显而易见的,毕竟这些都是盒子模型的一部分,且不会发生外边距折叠。

对其他的同级非浮动元素的影响

根据浮动元素会”脱离文档流“的性质,其原本在文档流中的位置会被后面的非浮动元素上移所占据。 这里会发生一个很有意思现象,跑上来的块元素的背景色会被这个浮动元素“部分”遮住,但是这个块元素里面的文字内容并不会被遮挡,从而出现文字环绕浮动元素的现象。(甚至于当浮动元素完全遮挡移动上来的块元素,其内容也不会被遮挡)。此外,如果浮动元素后面是内联元素也不会被遮挡,而是围绕在浮动元素周围。 因此,这里有很重要的一条结论:虽然浮动元素脱离了文档流,但是仍然会影响布局。

对其他的同级浮动元素的影响

根据浮动元素“照指定的方向移动,碰到其父容器边界或者是另外一个浮动元素就停下来”的特性,浮动的元素在其指定方向上浮动时,如果前方有另一个已经“浮动完成”的元素,他就会停下来。 如果浮动元素所在的那一行(按前一个已经浮动完成的元素高度计算该行高),所有浮动元素的宽度之和已经超出了容器的宽度,那么最后那个浮动元素会另起一行(紧贴着上面那一行高度的下面),重新朝指定方向浮动(我只是瞎猜的这个过程,具体发生了什么我没有查到相关资料),如果仍然碰见了另外一个浮动元素,则重复上面的过程,直至最后停靠在某个浮动元素的旁边或者是指定方向的父容器壁(除非浮动元素其本身设定的宽度就大于父容易的宽度,那么他就直接浮动在没有任何浮动元素且尽可能靠上方的那一行)。 资料上一般就几句话,我这好像还给他弄复杂化。这也是经常出现浮动的块没有出现在预期位置的原因,很可能就是宽度没计算好,多了那么一两个像素的缘故。这段话表述的很混乱,但是只想表达一个意思:弄明白一个浮动的元素最后摆放的位置。

清浮动

在实际的布局中,经常不设置父级盒子的高度,而是由内容自动决定的。浮动使元素脱离文档流,可能导致父级元素高度塌陷甚至消失的情况。 清除浮动主要有两种做法,一种是采用clear属性,这是真正意义上的清除;而另一种是使父元素正确计算其高度,也就是使父元素触发BFC。

clear

clear属性指定一个元素是否可以在它之前的浮动元素旁边,或者必须向下移动(清除浮动) 在它的下面。clear 属性适用于浮动和非浮动元素 为了减少标签数量,一般的清浮动类使用伪类实现

css
.clearfix { *zoom: 1; } /* 兼容 IE 低版本 */
.clearfix:after { content: "";display: table; clear: both; };

使用clear进行清浮动,可以指定该元素的对应方向上不存在浮动元素。

在CSS2.1中,使用clear的本质是引入了一个“清除区域”,清除区域是在元素上外边距之上额外增加了一个间距,且不允许任何浮动元素进入这个范围。这样造成的结果是设置了清浮动的元素的上边框边界推到了刚好越过浮动元素下边界的情况,出现“设置了上边距却没有出现间隔”的情况,解决这种情况的一个办法是设置浮动元素的下边距,相当于延长了浮动元素的下边界,从而达到预期的效果,具体的原理只有一句话:浮动元素的外边距边界定义了浮动框的边界(参考《CSS权威指南》)

BFC

让父元素触发BFC有多种方式,如果不是特定的布局,overflow:hidden就够用了。

css
.parent { overflow: hidden}

新增加的属性display:flow-root可以创建一个无副作用的BFC,可以更方便地进行BFC清浮动处理。

一些属性的计算规则

参考:一些属性的计算规则