前端布局方案主要有三种:
传统布局方案(借助浮动、定位等手段)
flex布局方案
grid布局方案
这些方案都能够解决布局问题,而且每个方案都有各自的理论基础,那么哪一个方案的基础理论可以称得上是前端布局基础?要回答这个问题,我们还得深入去了解这三种方案的特性。
传统布局方案,需要使用者熟练掌握元素的分类及布局特性、浮动原理和定位原理等众多基础知识,方能在解决各类前端布局问题时游刃有余,这不仅学习成本大,而且实现的复杂度也高,实现的CSS代码也不够精简、优雅。但由于其基础知识来源于CSS2,所以浏览器兼容性最好,对于用户是友好的。
flex布局方案,正是为了解决传统布局方案的种种不便,而提出的一种新型改进方案,它不再需要借助浮动和定位等布局手段,而是通过父元素(flex box)单方面配置相关的CSS属性来决定子元素的布局规则,且在大多情况下无需子元素(flex item)参与,就能完成子元素间的布局问题,不仅学习成本低(公司之前有几个后端工程师亦能快速上手),且大大简化了布局的实现复杂度,CSS代码也更加精炼。美中不足的是IE10才开始支持,且需要使用-ms-前缀(IE11无需)。
虽然现今的手机多使用的是现代浏览器,对flex支持度较好,然而并不是每一款手机都如此:笔者曾在一个移动端项目采用过flex布局方案,然而公司的测试同学在“华为荣耀5”的自带浏览器,检测到无法支持flex布局,我们能够跟测试的同学说,是这款华为手机的浏览器有问题吗?显然不能。于是故笔者在项目早期就及时放弃了flex布局方案,改用传统布局方案实现,避免了后面大规模的改动。
grid布局方案,是由微软提出,相对于传统布局方案和flex布局方案,它是一种二维布局方案,在IE10开始支持,但需要使用-ms-后缀(IE11+不再需要)。
总的来说,这三类方案都能基本解决日常的前端布局问题,且从易用性、灵活性和强大性来说,flex布局和grid布局更是未来的趋势。但是从当前各版本浏览器在用户市场上的使用情况和各方案的浏览器兼容性来看,传统布局方案对用户最友好,具有一定的不可替代性,所以我觉得,传统布局方案是最应该先掌握好的,尤其是对于在to B企业工作的前端同学来说。
2.1. CSS标准盒模型(或W3C盒模型)
一个web页面是由众多html元素拼凑而成的,而每一个html元素,都被解析为一个矩形盒,而CSS盒模型就是这种矩形盒的解构模型。CSS盒模型,它由内到外、被四条边界Content edge、Padding edge、Border edge和Margin edge划分为四个区域:Content area、Padding area、Border area和Margin area,在形状上,Content area(又称content-box)是实心矩形,其余是空心环形(空心部分是Content area),如下图所示:
CSS盒模型-区域划分图
2.2.1. box-sizing的作用
box-sizing,顾名思义,其作用与设置CSS box的尺寸大小有关,而CSS box又可细分为:
content-box(即content area)
padding-box(=content area + padding area)
border-box(=content area + padding area + border area)
margin-box(=content area + padding area + border area + margin area)
简单来说,box-sizing的作用就是告诉浏览器:CSS属性width和height是用于设置哪一种box的尺寸,在W3C标准中,box-sizing的值仅有content-box和border-box(firefox则额外支持padding-box)。所以,
当box-sizing的值为content-box(默认值)时,有:
width = content-width;
height = content-height;
当box-sizing的值为border-box时,有:
width = content-width + padding-left + padding-right + border-left-width + border-right-width;
height = content-height + padding-top + padding-bottom + border-top-height + border-bottom-height;
关于box-sizing的作用,还有另一种表述:告诉浏览器,是使用W3C盒模型,还是使用IE盒模型。
元素的分类及其布局特性
2.3.1. 元素的分类
从元素的布局特性来分,主要可以分为三类元素:block-level(块级)元素、inline-level(行内级)元素和inline-block-level(行内块级)元素,我们可以对其下个定义:
1 块级元素
display属性取block、table、flex、grid和list-item等值的独占一行显示的元素。
2 行内级元素
display属性取inline值的可在同一行内排列显示的元素。
3 行内块级元素
display属性取inline-block、inline-table、inline-flex和inline-grid等值的兼具块级元素和行内级元素布局特性的元素。
格式化上下文,它指的是具有某种CSS格式化规则(布局规则)的上下文环境,在这个上下文环境内的所有子元素,都将根据其特定的CSS格式化规则来进行排列。
我们可以给某个作为容器的元素指定特定的格式化上下文,也就是说我们可以定义一个具有特定布局规则的渲染区域。常见的格式化上下文有BFC(CSS2.1 规范)、IFC(CSS2.1 规范)、 FFC(CSS3规范新增)和GFC(CSS3规范新增),具体介绍如下:
BFC, 全称是block formatting context,它是一个独立封闭的渲染区域,在这个区域内的所有元素,从区域的顶部起,一个接一个地根据自身的布局特性进行排列:在这个区域内的块级元素 ,按从上到下的顺序显示,相邻的块级元素可以使用margin隔离,但在垂直方向上相邻的块级元素会发生margin合并;在这个区域内的inline-level或inline-level-block元素,则按从左到右的顺序显示(W3C组织说BFC内部的元素都是一个接一个地垂直显示,我觉得不是很严格,因为BFC内部也可以容纳inline-level和inline-level-block元素,所以这里我的解释和W3C还是稍微有一些不一样)。具有BFC格式化环境的元素,我们称之为BFC元素,可以说,BFC定义了BFC元素content区域的渲染规则。
看到这段描述,是不是觉得BFC的渲染规则,不就是文档流的默认布局规则吗?确实很像,但不完全等同。BFC元素内部的渲染规则和普通块级元素内部的渲染规则,还是有一些不同的,我们将在5.4.1.3. 特性一节详述。
2.4.1.2. 创建方式
创建BFC元素的方式有如下几种(摘自MDN BFC):
根元素或其它包含它的元素
浮动元素 (元素的 float 不是 none)
绝对定位元素 (元素的 position 为 absolute 或 fixed)
内联块 (元素具有 display: inline-block)
表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
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 应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。
定义
IFC, 全称是inline formatting context,其内部的元素,在水平方向上,一个接一个地显示;在垂直方向上,每个元素可以设置不同的对齐方式;IFC内部的元素,被一行行的矩形框所包含,这些虚拟的矩形框,我们称为行框(line box)。IFC的作用区域,可以看成是包含其所有子元素的行框组成的矩形区域。
创建方式
和BFC相比,它的创建方式是被动的、隐式的,是由所包含的子元素来创建:只有在一个区域内仅包含可水平排列的元素时才会生成,这些子元素可以是文本、inline-level元素或inline-block-level元素。
特性
1. IFC内部的元素,按从左到右、从上到下的顺序排布;
2. IFC内部的每个元素,都可以通过设置vertical-align属性,来调整在垂直方向上的对齐;
3. 包含这些内部元素的矩形区域,形成的每一行,被称为line box(行框,后面会详细介绍);
FFC和GFC
FFC(flex formatting context)和GFC(grid formatting context),分别是flex布局和grid布局的内容,这两个模块的内容非本文介绍的重点,所以感兴趣的同学可以自行google。
包含块(Containing Block)
定义
我们在设置元素尺寸属性(width、height、padding、margin和border)的百分比值或偏移属性(top、right、bottom和left)的值时,通常会有一个“相对参考系”,这个"相对参考系"一般是包裹着这个元素的块级祖先元素(一般是块级父元素)或离这个元素最近的非static(relative、absolute和fixed)定位的祖先元素。这些具有“相对参考系”作用的祖先元素,其容纳区域(cotent box或padding box),其实还有一个专门术语形容之,那就是包含块(在知识体系中有个包含块的概念,有助于加深对position定位原理的掌握)。
特别地,relative定位元素,其尺寸属性(width、height等)的“相对坐标系”仍是其包含块(块级祖先元素(一般是父元素)的content box),但是偏移属性(top、right、bottom和left)的“相对坐标系”则是其在文档流原来的位置。
2.5.2. ICB(initial containing block, 初始包含块)
定义
如前面所说,任何一个元素都会有一个包含块作为设置尺寸属性和偏移属性的“相对参考系”,而对于顶层的根元素<html />,没有任何元素包裹它,它的包含块是什么?它选取什么作为“相对参考系”?
其实根元素<html />是有包含块的,它是一个不可见的矩形框,W3C组织称之为ICB(initial containing block, 初始包含块)。以下是W3C组织对ICB对定义:
The containing block in which the root element lives is a rectangle called the initial containing block.
ICB的尺寸和起始位置(左上角坐标)
在解释ICB的尺寸和起始位置时,在这里先简单补充一个背景知识:连续媒体(continuous media)和分页媒体(paged media)。如何理解这两个概念?在视觉阅读层面,它们是展示内容的两种呈现方式。
连续媒体,就是采用连续展示内容的方式,它保持了展示内容显示的连续性(一页显示所有内容),我们可以在连续媒体的viewport(可视窗口)查看当前呈现的内容。特别地,浏览器窗口就可以看成是连续媒体,当内容的尺寸超过viewport时,读者可以通过平滑滚动的方式来阅读内容。
分页媒体,就是采用切页展示内容的方式,它将要展示的内容切分为等尺寸的多页(分页显示所有内容),我们可以在分页媒体的page area(页面显示区域)查看当前呈现的内容。特别地,像幻灯片、电子书阅读器,就可以看成是分页媒体,当内容的尺寸超过page area时,读者可以通过切页的方式来阅读内容;
对于属于连续媒体(continuous media)的浏览器窗口来说,ICB的尺寸为viewport(浏览器视窗),其起始位置为画布原点(canvas origin,即首屏的左上角,浏览器渲染数据后生成的内容文档可以看成是一张画布)。
对于分页媒体来说,ICB的尺寸为page area(关于ICB在分页媒体的起始位置,没有找到相关资料,但这个对于本文来说也不是重点)。
直观来看,根元素<html />的包含块ICB,就是“首屏”。
2.5.3. 不同定位元素分别对应的包含块
static和relative定位元素的包含块,为其块级祖先元素(通常是块级父元素)的content box;
absolute定位元素的包含块,为最近的非静态定位祖先元素的padding box,查无非静态定位祖先元素,那么它的包含块是ICB(即根元素<html />的包含块);
fix定位元素的包含块,为当前viewport(视窗);
在这里要强调的一点,ICB(初始包含块)是专有名词,它特指根元素<html />的包含块。不要将一个元素的初始包含块,错误理解为它的父元素。