首页 > 代码库 > CSS布局

CSS布局

一些基本概念

  • viewport: 展现网页的媒体,比如窗口或者某个区域,它的大小是有限制的,为了不被平台术语所束缚,我们给他起名viewport,中文意思就是视口。
  • canvas: 而我们在渲染网页的时候通常并不知道我们需要多大的空间,而且这些空间通常尺寸会超过viewport的大小,于是实际上我们需要设想一个无限大的画布来绘制我们的元素,我们把它称为canvas。
  • box: element(元素)和node(节点)是大家很熟悉的概念,当我们做布局计算的时候,通常会把节点变成box,一个节点可能产生多个box,伪元素也会产生box。
  • render tree: 对应于dom树,我们把box的包含关系构成的属性结构成为渲染树render tree

BFC

首先我们来考虑一个基本的情况:

<html>  <head><title>囧</title></head>  <body>    <div style="width:300px;height:50px;"></div>    <div style="width:300px;height:20px;"></div>    <div style="height:50px;"></div>    <div style="width:300px;height:50px;"></div>  </body></html>

之所以简单是因为这些div每一个只产生一个盒子,默认的display下,div产生块级盒,为了让块级盒按照我们的意愿排布,我们需要引入一个概念:block formatting context,简称BFC。

BFC其实是个相当简单的概念,然而因为实在中文中没啥合适的词来形容(你不觉得"块级格式化上下文"比BFC更让人觉得不知所云么?),这个概念已经被传得极其神秘:

在 CSS 面试中问 BFC 等概念,就如在 JS 面试中问闭包等概念一样,经常会刷掉一些真正优秀的人。 ——玉伯

甚至很多人连它是动词还是名词都搞不清,不信你可以搜索一下触发BFC,亲,BFC是名词啊……怎么它就能触发呢?

简单地说,BFC就是像例子里面一样顺次竖着排块级盒的一种上下文。竖着排盒子而已,竖着排盒子有什么理解不了的啊我X?

好吧平复一下情绪,我们继续说,canvas会设立一个BFC,这也是最外层的formatting context了,问题的复杂性在于有些块级盒内部也可以产生BFC(至少它必须也能包含块级盒),于是说BFC是可以嵌套滴:

<html>  <head><title>囧</title></head>  <body>    <div style="width:300px;height:100px;">      <div style="width:300px;height:20px;"></div>      <div style="height:50px;"></div>    </div>    <div style="height:50px;"></div>  </body></html>

不是所有块级盒内部都可以产生BFC,比如说要是这盒里面连块级盒都没有,都是行内盒那就产生IFC:

<html>  <head><title>囧</title></head>  <body>    <div style="width:300px;height:100px;overflow:visible;">      <span>囧</span><span>囧囧</span>    </div>    <div style="height:50px;"></div>  </body></html>

不过,只要它的子节点里面有一个块级盒,它就产生BFC,那些行内元素,会自动套一个匿名的块级行盒。

<html>  <head><title>囧</title></head>  <body>    <div style="width:300px;height:100px;overflow:visible;">      <span>囧</span><div>啦啦啦</div><span>囧囧</span>    </div>    <div style="height:50px;"></div>  </body></html>

又比如说overflow:visible的块盒就不产生BFC,不但不产生BFC,啥FC都不产生,它的子元素直接搞进自己外层的BFC鸟:

<html>  <head><title>囧</title></head>  <body>    <div style="width:300px;overflow:visible;">      <p style="width:200px;height:20px;"></p>      <p style="height:50px;">aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa</p>    </div>    <p style="height:50px;"></p>  </body></html>

overflow:visible这个限制只对所谓的块盒(既包含块级盒、自己又是块级盒)存在,有些盒内部也能包含块级元素,但是它本身又不是块级元素(比如display为table-cell、inline-block、或者盒本身是flex item等),因为外面不是BFC,所以它们不论如何一定会给包含的块级盒创建一个新的BFC出来。

同一个BFC里面的盒还会有margin collapse现象,详情见此文 《谈margin collapsing(外边距叠加)》

IFC

IFC里面可能有文字、行内元素混合排布:

<html>  <head><title>囧</title></head>  <body>    <div style="">      <span>囧</span>囧囧囧<span>囧囧</span>    </div>  </body></html>

文本的排布是个很复杂的事情,在dom树中,我们存储的是字符和字符串,而在render tree中,我们要使用一个新的概念:字型(glyph)。字体文件(通常是ttf,ture type格式的)中包含着不同字体下字符到字型的规则。

一个字型有很多属性,下面这张图来自freetype,它是大部分浏览器使用的文字排版基础库:

<iframe id="iframe_0.19726156948619789" src="data:text/html;charset=utf8,%3Cstyle%3Ebody%7Bmargin:0;padding:0%7D%3C/style%3E%3Cimg%20id=%22img%22%20src=%22https://a248.e.akamai.net/camo.github.com/4db4d15b9b7654b2ec71c0bf08b7b861f00e0300/687474703a2f2f7777772e66726565747970652e6f72672f6672656574797065322f646f63732f7475746f7269616c2f6d6574726963732e706e67?_=3072929%22%20style=%22border:none;max-width:1521px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById(‘img‘);%20window.parent.postMessage(%7BiframeId:‘iframe_0.19726156948619789‘,width:img.width,height:img.height%7D,%20‘http://www.cnblogs.com‘);%7D%3C/script%3E" frameborder="0" scrolling="no" width="320" height="240"></iframe>

详情请看这里。

图中的那根加粗的X轴就是传说中的基线(base line)了,文字默认是按照基线对齐的。

下面这个demo展示了一个IFC中的几条重要的基准线:

 
 great it works!2囧啊

在水平方向,文字的排布主要受到以下属性影响

  • word-break, word-spacing, word-wrap
  • letter-spacing
  • white-space
  • text-align
  • hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphenate-limit-lines, hyphens
  • justify-content
  • ……

总之很复杂,讲也讲不完,而且大家又不要写浏览器所以大家就大概望文生义一下好了(po主被扔蛋中……)。

对于一个正常的"行盒"来说,文字总是垂直居中排布的。

在纵向,文本排布主要受line-height属性影响,在有行内盒参与的情况下就复杂了,

一个行内盒的vertical-align可以有这样几种取值:

  • baseline
  • sub
  • super
  • top
  • text-top
  • middle
  • bottom
  • text-bottom

它也可以是某一指定高度。根据行内盒的对齐方式,它有可能把这行盒向上或者向下"撑大",造成超过行高的情况。

所以行盒在BFC中占据的实际高度,很可能大于line-height。

文字的baseline很好理解,而行内元素则比较复杂,特别是当行内元素自身又是容器的时候:

<html>  <head><title>囧</title></head>  <body>    <div style="overflow:visible;">      <span>囧</span>囧囧囧<div style="display:inline-block;width:300px;height:300px;"></div><span>囧囧</span>    </div>  </body></html>

<html>  <head><title>囧</title></head>  <body>    <div style="overflow:visible;">      <span>囧</span>囧囧囧<div style="display:inline-block;width:300px;height:300px;">123<br/>123<br/>123</div><span>囧囧</span>    </div>  </body></html>

浮动

浮动是个行级的行为,当遇到浮动元素的时候,会首先"假装"它是个行内元素进行排版,排好后就往浮动的方向挤到挤不过去为止(遇到边界或者其它浮动元素)。

<html>  <head><title>囧</title></head>  <body>    <div style="">      <span>囧</span>囧囧囧<div style="display:inline-block;width:300px;height:300px;float:left"></div><span>囧囧</span>    </div>  </body></html>

<html>  <head><title>囧</title></head>  <body>    <div style="">      <span>囧</span>囧囧囧<div style="display:inline-block;width:300px;height:300px;float:left"></div><span>囧囧</span><br/>
<div style="display:inline-block;width:300px;height:300px;float:left"></div> </div> </body></html>

某一方向有clear的时候,浮动元素总是挤到边界,在垂直方向上的行为类似"换行"。

<html>  <head><title>囧</title></head>  <body>    <div style="">      <span>囧</span>囧囧囧<div style="display:inline-block;width:300px;height:300px;float:left"></div><span>囧囧</span><br/><div style="clear:left;display:inline-block;width:300px;height:300px;float:left"></div>    </div>  </body></html>

排好一个浮动元素之后,这一行就要重排一次。所以说浮动元素会造成行级的reflow。重排的时候,行盒会躲开浮动元素。之后的块级盒(不论是行盒还是其它盒)也都会躲开浮动元素排布。

最后赠送文中使用的查看盒边框的小工具,欢迎自己来写网页看盒子:

CSS布局