本书可以说是深入理解 CSS 原理的必读之书,作者张鑫旭也是十年磨一剑,编写了这本独一无二的 CSS 书籍。读其他 CSS 的书籍只能做到知其然,而读这本书却能做到知其所以然。没别的称赞,果断推荐,而且我觉得应该所有的前端开发者都应该好好拜读这本书,以强化自己的 CSS 知识和认知。
本书一开始先给大家构建了一个 CSS 的时间观,让读者知道 CSS 在这个缤纷的网络世界中扮演了什么样的角色,有什么价值。接下来,全书以“流”这个 CSS 设计时最核心的思路,来讲了我们开发常用的一些样式和语法,它们如何构建一个“流”的世界。除了“流”之外,本书也讲了其他的内容,不过那不是我关心的重点。
最后,本书讲解的内容都是 CSS 2.1 版本的,发布距今都已经 10+ 年了,可以说是最常用、最普及的知识了。CSS 3 的知识本书没有涉及,作者没有解释,但是读到最后我想明白了这个原因 —— 本书立意独特,讲解的都是作者精心准备、读者常不理解或者犯错误的知识点,例如float
的使用、line-height
的认知等。而这些易理解出错的方面,CSS 2.1 中有很多,但是 CSS 3 中却很少。因为,CSS 2.1 很多语法的设计初衷和现在的使用场景都不一致,导致很多误解或者误用,而 CSS 3 设计用意和场景完全匹配,误用很少。因此,CSS 3 也就没得讲了,干脆就不讲了。
当然,以上的这个原因,纯属我个人的猜测。如有雷同,也属于巧合。最后,再重点强调一下 CSS 2.1 的设计初衷:
CSS 2.1 是为图文展示而设计的。
书中这部分内容比较少,但是作者提出来的这个比喻我觉得特别贴切。这么一比喻,就把 CSS 与浏览器、操作系统的关系给一下子说清楚了,生动形象。
- 操作系统是宇宙,浏览器是王国,css 是一个一个的魔法师,例如
width
魔法师、font
魔法师…… - 不同的操作系统就是不同的平行宇宙,比较强大的如 windows, os X, ios, android
- 不同的宇宙中王国的命运不一样,例如 os X 宇宙中 safari 王国比较强大,而 windows 宇宙中 safari 王国就异常没落
- 不同宇宙或者不同王国中,魔法师在法力表现上也不尽相同,如 chrome 王国和 firefox 王国中的魔法师,有些法术可能就不太一样(即浏览器兼容性)
书中这部分也内容比较少,但是我觉得“流”这个概念对于本书的阅读和理解非常总要,因此就单拿出来一个大标题来重点说明,可能文字并不多。
书中提到,SVG 的标准在 2003 年就发布了,而 CSS 2.1 的标准直到 2007 年才发布,而那时候 web 1.0 已经很普及了,但是最终还是 CSS 战胜了 SVG 成为了网页布局的主导语言。究其原因,第一是因为 CSS 更加适合图文布局,第二就是 CSS 的“流”特性。当然,SVG 后来换发第二春,成为了 canvas 直接的竞争对手,也是因为网页图形化的兴起,这是另外一回事儿。
“流”,我理解就是它能在不同尺寸的容器中,合理的处理图文信息的这种不确定性,其实就是流动特点。图片可大可小,文字可多可少、字体多变,再加上尺寸多变,这种不确定性只能通过“流”去解决。例如一个不确定尺寸的容器,只能用水来完全填满,而水就具有流动特性。具体 CSS 的例子就不列举了,下文遍地都是,在这里也列举不全。
width:auto
使用场景很多,表现形式也很多,书中提到以下几种情况:
- 充分利用可用空间,即流动性。例如
<div>
会自动铺满整个父容器。 - 收缩和包裹,例如浮动和绝对定位的“包裹性”,
- 收缩到最小,例如表格中每个单元格的文字,会自动收缩
- 超出容器限制,例如其中的内联元素设置了
white-space: nowrap
其中最常用的就是,让<div>
铺满父容器时,不要故意设置width:100%
,因为还可能会有 padding margin border 的宽度影响,让其自适应就好了。
针对盒子模型来说,width
默认作用域content-box
上,即内容区域。现在的普遍做法是增加box-sizing: boxer-box
。不过作者的另外一种解决方案也非常体现“流”特性。
.father {
width: 180px;
}
.son {
margin: 0 20px;
padding: 20px;
border: 1px solid;
}
最后作者解释了box-sizing
设计的初衷,让我读来眼界大开。因为我们专业于一门技术,不仅仅要知道 what 和 how ,还要知道 why 。书中提到,box-sizing
的设计初衷是为了解决替换替换元素宽度自适应问题。所谓的“替换元素”可以简单的理解为各种表单元素,例如input
textarea
video
object
还有 img
,这些元素的样式最难控制。
替换元素有一个特点,例如textarea
,宽度不容易被控制,不具有流动性。即,div
如果不设置宽度会撑满父容器,而textarea
则不会。这种情况下,如果没有box-sizing
的话,我们没法通过控制宽度来实现自适应。例如,如果针对textarea
设置width:100%
,是作用域content-box
,再加上padding
border
宽度,就超出父元素了。
CSS 的默认“流”是水平方向的,宽度是稀缺的,高度是无限的
有时候设置height:100%
无效,是因为只要父元素是height:auto
,子元素在文档流中(不是float
或者absolute
),那么子元素的高度百分比就被忽略 ,记住这条原则。要想解决这个问题,就得让父元素必须有一个可以生效的高度值,例如:
html. body {
height: 100%;
}
div {
height: 100%;
}
当然,直接对div
设置绝对定位,也可以让height:100%
生效。不过注意,绝对定位的宽高百分比计算是相对于padding-box
的,而正常情况下的计算则是相对于content-box
的。
块级原则负责结构,内联元素负责内容
内联元素不仅仅是display: inline
,inline-block
inline-table
也算是内联元素,其表现就是可以和文字在一行显示。
书中提到了“内联盒子模型”,可以对比一下普通的盒子模型。不过这个模型日常使用中基本不会关心到,因此简单列一下,详细内容去看书。
- 内容区域
- 内联盒子
- 行框盒子
- 包含盒子
最后书中提出了一个非常非常非常重要的概念 —— 幽灵空白节点(作者起的名字),这是日常开发中实时遇到但却实时被大家忽略的一个问题。该问题触发有一个前提,就是必须是 HTML5 文档声明。
<!doctype html>
<html>
如下例子,这个div
的高度并不是 0 ,而是 18px 。命名里面什么内容都没有,为何有高度呢?
<div><span></span></div>
其实可以这么理解,在<span>
前面有一个宽度为 0 的空白字符(即幽灵空白节点),它撑起了整个内联元素的高度,也就撑起了<div>
的高度。
这个空白节点的影响,会出现于没有文字的内联元素场景中,而且基本是以“坑”的形式来出现的。例如,下面代码执行后,img
和div
的下边界会有一个空白区域,这是为何?
<div>
<img src="xxx.png"/>
</div>
盒模型是 CSS 的基础知识,它包含四个盒子:content-box, padding-box, border-box, margin-box ,书中也是按照这个顺序来写的,下面针对一些印象比较深的内容做记录。
替换元素,即可以被替换的元素。常见的有<img>
<object>
<video>
<iframe>
<textarea>
<input>
<select>
。替换元素有以下几个特点:
- 内容可替换,例如图片 src 可随意换
- 基本样式 CSS 很难改变,你无法通过 CSS 将
<select>
做的像某些 UI 组件那么漂亮 - 有自己的尺寸,基线是下边缘,
<video>
默认就有自己的尺寸,<textarea>
可以通过row
和col
修改尺寸,<img>的基线就是其下边缘
简单来说,替换元素有自己的一套尺寸计算逻辑,并不符合“流”特性。
块状元素的 padding 比较简单。对于内联元素的 padding ,水平方向会影响布局,而垂直方向可增加其可视区域却不会影响布局。这也是好多开发者以为内联元素的垂直 padding 不起作用的原因。此处书中提到了两个例子,都是在不改变布局的情况下,第一是增加链接的可点击区域,第二是对锚点标题设置边距,都很实用。
padding 不支持负值,但是支持百分比。注意,无论是水平方向还是垂直方向的百分比,都是根据元素的宽度计算的。
如果该元素是流式填满父容器的元素,则 margin 设置负值可以改变其可视区域,但不影响布局。例如书中 84 页的示例,一行有三个卡片,最后一个和父容器没有间隙,比较常用。
margin 设置百分比值,和 padding 一样,无论是水平还是垂直方向,都是相对于元素宽度做计算的。
垂直方向的 margin 合并是开发中常见的“坑”,下面列出三种常见的场景并给出示例:
第一种情况,相邻兄弟元素的 margin 合并,这是最常见的,示例如下。这个问题并不会造成什么困扰,因此不用规避。
<style>
p {margin: 1em 0}
</style>
<p>第一行</p>
<p>第二行</p>
第二种情况,父元素和第一个/最后一个子元素,就是经常被问到的margin-top
失效的问题。示例如下:
<div>
<div style="margin-top:80px">内容</div>
</div>
该示例会触发的问题是,子元素的margin-top
会作用域父元素上,和我们书写的预期完全不一致。解决这个问题,有以下途径:
- 让父元素 BFC
- 给父元素设置
border-top
- 给父元素设置
padding-top
- 父元素和子元素之前添加一个内联元素作为分离
第三种情况,空块级元素的 margin 合并。如下例子。
<style>
.father {
overflow: hidden;
}
.son {
margin: 1em 0;
}
</style>
<div class="father">
<div class="son"></div>
</div>
该示例中,子元素的margin-top
和margin-bottom
会合并在一起,导致子元素的高度只有1em
—— 前提是子元素是空的,没有任何内容。
其中这种场景,通过下面的例子解释就很清楚了。下面的示例中,AAA
和BBB
之间的距离就应该是10px
,而不是离着很远。
<style>
p {
margin: 10px 0;
}
</style>
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
说完了垂直合并,再看看margin: auto
带来的流动性。margin:auto
是为了填充闲置的尺寸而设计的,规则是:
- 如果一侧定值,一侧 auto ,则 auto 为剩余空间大小
- 如果两侧都是 auto ,则评分剩余空间
例如靠左对齐
.son {
width: 200px;
margin-right: auto
}
例如水平居中对齐
.son {
width: 200px;
margin: 0 auto;
}
例如水平垂直居中对齐
.son {
posotion: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: 200px; height: 100px;
margin: auto; /**将上下左右的空白区域都均分并自动填充**/
}
这部分没有要记录的内容。
内联元素有两个知识点 —— line-height
和vertical-align
line-height 是两个基线之间的距离,vertical-aligin 的默认值就是基线(baseline),基线到底是什么?基线,就是一行文字中字母x
的下边缘。
内联元素的高度是由 line-height 决定的,并不是 font-size 。而且,对于非替换元素的内联元素,其高度完全由 line-height 决定,跟 fontsize border padding 都没有关系。但是,line-height 却无法决定替换元素的高度,例如一行文字中有图片,图片的高度时不受这一行的 line-height 影响的。
line-height 可以让单行或者多行文字近似的垂直居中。因为 vertical-align 默认是基线对齐,而基线对齐并不一定就是垂直居中对齐。想要实现绝对的垂直居中,还需要 vertical-align 帮忙。如下代码(注意看注释):
<style>
.box {
line-height: 120px; /* 对内联元素前面的空白幽灵节点起作用,使其能撑开整个盒子 */
backgroud-color: #ccc;
}
.content {
display: inline-block; /* 使当前元素为内联元素,因此前面会出现空白幽灵节点 */
line-height: 20px;
vertical-align: middle;
}
</style>
<div class="box">
<div class="content">段落内容。。。</div>
</div>
以上方法,也可以用于图片的垂直居中,只需要将<div class="content">
换成<img/>
即可。
line-height 的继承无处不在,而line-height 不同值类型对于继承也会有不同的影响,这一点经常被考察到。例如
line-height: 50px
这种固定尺寸,往下继承的就是50px
line-height: 1.5
这种数值,往下继承的就是1.5
这个倍数line-height: 150%
或者line-height: 1.5em
这种方式,往下继承的是当前计算出来的数值,而不是倍数,这里一定要注意!!!
vertial-align 只有在内联元素以及 table-cell(即表格单元格)元素中才能生效,vertical-align 值比较多,可参考 http://www.w3school.com.cn/cssref/pr_pos_vertical-align.asp 。其中,书中重点强调了可使用数字或者百分比,用处比较大。例如,当文字和小图标一行时,小图标位置偏上,可以给小图标设置line-height: -5px
让其下移,书中 128 页。
书中这里还讲解了好多 vertical-align 的内容,不过我没有继续往下看,因为感觉这些内容已经太深太专业了,和日常的开发联系并不大,因此就先跳过。
本章讲解的元素没有流动特性,而是对流动特性的破坏,但是这也和“流”有关。主要与floot
BFC absolute
相关。
首先,float 是一个很古老的样式属性,它设计的初衷就是实现简单的图文混排(像报纸一样),并没有考虑其他的使用场景。而现在 float 被大家大量的用来做布局使用,做不是它设计初衷的事情,肯定会出现很多问题,也失去了流动性。因此,float 是魔鬼,尽量不要触发它,能躲则躲。CSS3 慢慢普及之后,我们可以使用 flex grid 来做布局,不要再用 float 了。
元素设置了 float 会有以下几个特性:
- 包裹性。本来元素是自动撑满整个父容器的,设置了 float 之后就会收缩,紧紧包裹住内容。
- 触发 BFC(是其中一个条件)
- 破坏文档流,使父元素塌陷
- 没有任何 margin 合并。都跳出文档流了,肯定也不符合流式布局的特点了。
float 和流式布局也可以结合使用,例如左右布局,左侧元素宽度60px
并且设置float
,右侧设置margin-left: 70px
即可,书中 156 页。
最后,和 float 分不开的还有 clear ,基本一个clear-fix
就能搞定了。
BFC,block formatting context,跨级格式化上下文。如果一个元素触发了 BFC ,那其内部元素无论设置什么样式变化,都不会对元素外部产生影响。BFC 元素不能出现 margin 重叠,也不可能收到 float 的影响。能触发 BFC 的条件是:
<html>
跟元素- float 不是 none
- overflow: auto/scroll/hidden
- display: table-cell/table-caption/inline-block
- position: fixed/absolute
即,只要符合以上条件之一,都不需要使用clear:both
来清除浮动,因为 BFC 元素不会收到浮动影响布局。
一般用于触发 BFC 的条件是overflow: hidden
,它简单好用又不会对布局产生影响。float 结合 BFC 可以写成:
<style>
.left {float: left}
.right {float: right}
.bfc {overflow: hidden} /* 专门定一个一个 bfc 的 class */
</style>
<div class="bfc">
<img src="xxx.png" class="left"/>
<p class="bfc">文字内容……</p>
</div>
absolute 也具有破坏性和包裹性,这一点和 float 一样。absoute 还有一个重要的特性是无依赖,书中 184 页讲到,因为我之前了解过这块,因此就不再写了,但是确实很重要很常用。
另外,absolute 和 fixed 在和overflow:hidden
一起使用时,可能会遇到各种无法隐藏的问题,因此不要一起混用。如果遇到 absolute 和 fixed 元素需要裁剪时,可使用clip
语法,不会出问题。
最后,absolute 可体现流体特性,从一个垂直居中对齐即可体现,即结合margin:auto
,上文已经写了这个 demo 了,书中 202 页有。
其他和流体布局关系不大的内容,读来对我的印象没有那么深,用起来也不容易出现误解和问题,不重点关注了。
CSS 是“流”的世界,这包括:
- 元素尺寸
- 盒子模型
- 内联元素
- 流的破坏和保护(float、BFC、absolute)
而这,就是 CSS2.1 的最核心内容。