01 一道小菜
CSS很难,这应该是绝大多数Web开发人员的共识。
什么?你并不觉得很难?那我就先上一道小菜,请君品尝。
这是个乍一看,让人觉得很诡异的案例……
算了,本来想滔滔不绝介绍一番,但一想到Linux之父Linus大神所说的“Talk is cheap, show me the code.”,那我还是“废话少说,放码过来”吧,各位接招。
HTML部分:
<p>ABC XYZ</p>
<div><span>abc xyz</span>
</div>
<p>ABC XYZ</p>
CSS部分:
p {width: 200px; margin: 0; background: silver;
}
div {width: 200px;line-height: 0;font-size: 50px;background: gold;
}
span {font-size: 0;
}
这个案例的“诡异”之处在于,当子元素span的font-size属性值为0,因而其高度也为0时,竟然撑开了父元素div的高度。如下面的图所示。
而当子元素span的font-size属性值不为0,因而其高度也不为0时,比如删除子元素的font-size属性,这种情况下span元素的font-size属性值默认继承自父元素div,为50px,其他则完全不变,反而没有撑开父元素div的高度。如下面的图所示。
这也太反常,太违反直觉了吧?难道是浏览器有bug?
但在主流浏览器中,Chrome、FireFox、Edge、Opera都一样,甚至连IE8都一样,貌似就Safari不同(考虑到Safari已经沦落为了新一代的IE,这倒也不奇怪)。
好,坑先挖到这里,各位慢慢爬坑,期待各位的爬坑报告。
02 CSS难在哪里
很多前端初学者,通常一开始对CSS都很不以为然,以为看看w3school,顶多再读一两本专著(比如《CSS权威指南》、《精通CSS》之类的),就一定不在话下了。
但是,刚开始这样想的人,逐渐地一定会被惨淡的事实教育得重新做人,最终身心备受打击,严重的甚至于开始怀疑人生[流汗]
先一起来观摩一下知乎里的这些问题:
CSS真有这么难?
我觉得主要原因在于,重视不足,说白了就是一开始太小看CSS了。
也确实,说CSS是一种计算机语言都非常勉强,不就是对HTML元素设置一些样式属性嘛。
然而,CSS表面的看似简单,掩盖了由于历史原因所造成的问题:属性之间互相影响的情况非常严重;这用软件工程的术语来说,就是属性之间耦合得非常严重,这是CSS复杂性的根源。
所谓“历史原因”,主要应该是由于预估不足、发展太快所导致的。
一开始想得比较简单,不就相当于将报纸杂志搬到网络上吗?无非是多了可以跳转的链接,本质上还是图文排版啊。
但随着互联网的迅猛发展,Web页面所承载的内容越来越复杂,其功能要求也越来越丰富,仅仅图文排版是搞不定了,于是在原来的基础上加各种功能。
结果就是,CSS属性不断增长膨胀,而为了保持向下兼容,却又只能在原来的基础上修修补补,没办法推倒重来。
这就好比一个小孩的衣服,随着小孩身体的不断疯长,功能要求也越来越高,既要保暖,又要护体,还要审美,但却又不能扔掉重做,只能在原来的基础上不断地修改,不断地一层层打补丁......
想想这个难度,想想这件衣服结果会怎样?
因此,实践中,当你时不时被属性之间鬼畜般的相互影响搞得茫然无措、筋疲力尽时,如果能想到这一层,就能多一分理解,多一分冷静。
03 磨刀不误砍柴工
当然,光有理解和冷静,是无法解决问题的。
但在这些宝贵的理解和冷静的基础上,“磨刀不误砍柴工”,花点时间,深入了解CSS的底层原理、运作机制和历史沿革,你一定会不断地发出“啊,原来是这样”的感慨。
之后,不敢说从此就一定顺风顺水、一马平川了,但至少会自信很多,会多了一种像是庖丁解牛般尽在掌握的掌控感,进而给你带来“提刀而立,为之四顾,为之踌躇满志”的极大满足感。
事实上,正因为很多人对CSS太过于轻视,学习又不得法,导致所谓“易学难精”的CSS,可以说是成了优秀Web开发人员少有的护城河,因为同等的CSS经验很难通过同等的编程经验、工程经验来换取。
也就是说,就算你写了多年的代码,就算你熟练掌握了多门语言,在CSS面前,都得重新刷新自己的认知。
但如果你一旦掌握了CSS的规律和精髓,无疑将脱颖而出、鹤立鸡群、茫然四顾、独孤求败……好吧,这有点自我膨胀了[捂脸]
因此,CSS值得你花时间。
而且,跟着我的思路,我们一起来刨根究底,相信你可以少走很多弯路,少花很多时间。
04 内容说明
这里需要说明一点,本“刨根究底CSS”系列文章,不是针对完全没有CSS经验的人而写的。
因此,在看本系列文章之前,至少得对CSS的基本概念和基础语法有所了解,最好是还刷过一遍w3school中的入门课程。
而如果你是在实践中积累了不少困惑,遇到了瓶颈,被CSS虐了千百遍的Web开发人员,那就最适合不过了。
也因此,本“刨根究底CSS”系列文章,不会按照惯常的写法,逐个对CSS属性进行介绍,因为这些直接到W3C官网和MDN上查看就可以了,用不着我再复制粘贴一遍。
当然,毕竟是讲解CSS的文章,如果完全脱离对CSS属性的介绍,这文章也就没法写了。
只不过,对关键CSS属性的介绍,将穿插在对CSS底层原理和运作机制的说明之中,而不是单纯地简单罗列出来。
总体而言,就是一方面站在浏览器的角度,尽量将浏览器通过CSS属性将一个又一个的框框最终绘制出来的过程梳理一遍;另一方面是站在Web开发人员的角度,尽量从实践中的棘手问题入手,看看到底该怎么来理解问题,又该怎样来解决问题。
具体来说,站在浏览器的角度,Web网页本质上就是由元素框套着元素框构成的,因此如果要从CSS的底层原理和运行机制层面刨根究底地彻底说透,则必须从视觉格式化模型的角度入手,将以下几方面的内容讲清楚:
1)元素框分类:即元素框主要分为哪几类,各有什么区别,重点内容包括块级框、行内级框、匿名框以及其他各种特殊框等。
2)元素框模型:即元素框主要由哪几个部分构成,重点内容包括内容框、补白框、边界框、边距框与box-sizing属性等。
3)元素框定位:即元素框是如何确定位置的,重点内容将包括视口(布局视口、设备视口、理想视口)、包含块,以及常规流定位、浮动定位、绝对定位等。
4)元素框大小:即元素框的大小是怎样计算的,重点内容包括宽度、高度、边距计算,行高计算,以及垂直对齐(尤其是垂直居中对齐,相信这是很多人的噩梦)、字体与字体度量(这个要彻底讲清楚,要死很多脑细胞,但一旦弄懂了,就会有种打通了任督二脉的感觉)等。
注:垂直对齐、字体与字体度量看似与元素框大小的计算无关,但实际上这是严重的误解,这应该是CSS最难的部分,也是让Web开发者最为抓狂的部分。
5)元素框关系:即元素框相互之间的关系(一般也称之为布局关系)是怎么处理的,重点内容包括元素框与其祖先框、后代框以及同级兄弟框之间的关系处理,其中关键在于理解边距折叠、层叠上下文、格式化上下文FC以及各种格式化上下文与其所在的包含块之间的关系。
注:格式化上下文FC,包括块格式化上下文BFC、行内格式化上下文IFC、表格格式化上下文TFC、弹性格式化上下文FFC、网格格式化上下文GFC等。
这样一梳理,就可以发现,只要紧紧抓住元素框的生成与绘制这条主线,就能高屋建瓴、纲举目张,将以往觉得零零碎碎、杂七杂八、不成体系的各种CSS属性和奇技淫巧贯通起来,从而理解起来圆融无碍、如环无端。
当然,若只是站在浏览器的角度,将底层原理和运行机制理解得通透彻底,虽然可以获得大局观,理解也更为体系化,但这也还是不足够的。
还应站在Web开发人员的角度,从解决实践中的棘手问题入手,从而使用起来得心应手、顺手拈来。这就必须从CSS工程化的角度入手,将以下几方面的内容讲清楚:
1)层叠规则;
2)选择器及其权重计算与优先规则;
3)命名空间;
4)模块化;
5)预处理与后处理;
6)媒体类型与媒体查询;
7)度量单位与值;
8)伪元素与伪类;
9)变量与函数;
10)文本排版;
11)布局设计(浮动布局、流式布局、弹性布局、网格布局、响应式布局等);
12)针对Retina显示屏的特殊适配;
13)书写规范;
14)CSS Lint;
15)性能优化。
以上两个角度方面的内容,显然是不可能绝对分开的。因此,这两方面的内容将会互相穿插来讲。
不过,前期会主要从浏览器角度来讲解,同时穿插少部分涉及从Web开发者角度来讲解的内容;后期会主要从Web开发者角度来讲解,同时穿插少部分涉及从浏览器角度来讲解的内容。
05 CSS级别
由于前面说了这么多,已经占了不少篇幅,接下来暂时不说其他内容,先简单说一下CSS级别。
记得有一次,有个同事问我:“JavaScript有ES5、ES6、ES7、ES8等不断升级的版本,CSS为什么只有CSS2、CSS3,却没再听说有CSS4、CSS5等升级版本呢?”
这确实是个有意思的问题。CSS在对待版本的问题上,很有些“特立独行”。
事实上,CSS目前不使用传统意义上的版本来进行区分,而是使用级别来进行区分。
每一级别CSS都建立在前一级别的基础上,改进了定义并添加了特性。每个较高级别的特性集是任何较低级别的超集,并且较高级别的特性集所允许的行为是较低级别所允许的行为的子集。
因此,符合较高级别的CSS的用户代理也符合所有较低级别的CSS。
05.01 CSS级别1(CSS Level 1)
由于CSS 1规范已被W3C的CSS工作组(CSS Working Group)认定为已过时,因此,CSS级别1被确定为:CSS 1规范中所定义的所有特性(包括属性、值、at-Rule等),但这些特性的语法和规则等则以CSS 2.1规范中的为准。
注:CSS1规范在早期相当于是CSS 1.0版,CSS 2、CSS 2.1与之类似,在早期相当于是CSS 2.0版和CSS2.1版。随着CSS引入级别的概念之后,级别可以是跨规范的,并且逐渐淡化并最终弃用了版本的概念。
另外,sytle属性规范定义了特定元素的style属性中的CSS样式所应遵循的规范。
注:
所谓style属性,即元素的style属性,其中所书写的CSS样式,被称为行内样式(inline style),也称为行间样式,比如:<p style="color: red; font-size: 20px;...">...</p>,p元素的sytle属性中所定义的“color:red; font-size: 20px; ...”样式,就是这里所说的style属性样式。
style属性规范主要规定了style属性中的CSS样式与常规的CSS样式在一致性上的要求,以及在层叠计算时的优先规则等等。
05.02 CSS级别2(CSS Level 2)
虽然从技术上讲,CSS 2规范是W3C推荐标准,但它在W3C定义其为候选推荐阶段之前,就已经进入了推荐阶段。
注:
候选推荐阶段为推荐阶段的前一个阶段。
注意,W3C组织作为一个国际性的互联网技术协作组织,只有标准的推荐建议权,并没有标准的强制执行权,所以即便是该组织所制定的正式规范也只是称之为推荐标准,不过目前几乎所有主流浏览器都基本上遵循了这些推荐标准。
之前微软的IE浏览器有很多就没有遵循W3C标准,而是自搞一套,后来IE也就因此而被逐渐边缘化了,所以最后连微软自己都只好放弃了IE,而重新开发了遵循W3C标准的Edge浏览器。
现在苹果的Safari浏览器又有步IE浏览器后尘的趋势,被很多Web开发者戏称为“新一代的IE”。
随着时间的推移、实践检验和进一步审查,暴露了CSS 2规范中的许多问题,因此CSS工作组没有继续扩展已经很庞大的勘误表,而是选择了定义CSS Level 2 Revision 1(CSS 2.1)。如果CSS 2与CSS 2.1这两个规范之间有任何冲突,应以CSS 2.1为准。
一旦CSS 2.1成为候选推荐标准,即便不是处于和CSS 2相同的正式稳定阶段(CSS 2已经是推荐标准),但实际上就有效取代了CSS 2推荐标准。CSS 2中的功能如果在CSS 2.1中被删除了,则应视为处于“候选推荐”阶段。
但请注意,其中许多功能已经或将被纳入CSS级别3工作草案中。如果CSS级别3达到候选推荐阶段,那么CSS 2中的定义就过时了。
CSS 2.1规范定义了CSS级别2,而style属性规范则定义了特定元素的style属性中的样式所应遵循的规范。
05.03 CSS级别3(CSS Level 3)
CSS级别3是在CSS级别2之上,一个模块一个模块地单独构建的,使用CSS 2.1规范作为其核心。每个模块添加功能和/或替换部分CSS 2.1规范。
CSS工作组希望新的CSS模块只是添加功能并改进定义,而不会与CSS 2.1规范相冲突。当每个模块完成后,它将插入到现有的CSS 2.1系统以及之前已完成的模块中。
从这个级别开始,模块被独立地分级别定义。例如,选择器级别4(Selectors Level 4)很可能在CSS行模块级别3(CSS Line Module Level 3)之前完成。
CSS级别2中不存在的模块(即没有等效项的模块),从级别1开始;CSS级别2中已存在的模块,其更新从级别3开始。
05.04 CSS级别4及以上级别(CSS Level 4 and beyond)
没有CSS级别4和以上级别。独立的模块可以单独地达到级别4或更高级别,但整体的CSS语言不再有级别4。因此称为CSS级别4或CSS 4都是不正确的。
实际上,“CSS级别3”作为一个术语,也仅用于将其与以前的“CSS级别1”和“CSS级别2”这样的单一整体级别区分开来,而并非真的存在这么一个CSS语言的单一整体级别。
(未完待续)