css 简介
前边介绍 JavaScript 的简史,本章我们介绍另一个跟 HTML 形影不离的技术 CSS,它的存在感视乎没有 JavaScript 那么强。如果说 JavaScript 是用户交互的发动机,那么 CSS 应该就是衣服了,主要工作是修饰网页。
1. 历史
1.1 质疑
当您想到 HTML 和 CSS 时,您可能会认为他们是一个共同体。事实上在 Tim Berners-Lee 于 1989 年首次创建万维网之后的多年中,没有发明过 CSS 之类的东西,最初的 HTML 根本无法使用样式修饰网页。
WWW 邮件列表的存档中埋藏着一个臭名昭著的帖子,它是由 Marc Andreessen(网景浏览器联合创世人) 于 1994 年编写的。在帖子中,Andreessen 表示,由于无法用 HTML 对网站进行样式设置,因此当被问及视觉设计时,他唯一可以告诉 Web 开发人员的是 “很麻烦。” 在 10 年后,CSS 逐渐被一个新的网络社区全面采用。这期间发生了什么事?
1.2 寻找
关于如何修饰网页历史上很多人提出过很多质疑,但是,对于 Berners-Lee 来说,这并不是优先考虑的事情。相反,很多社区的网页开发人员提出过几种技术选型方案,其中最著名的是 Pei-Yaun Wei,Andreesen 和 HåkonWium Lie。以 Pei-Yuan Wei 为例,他于 1991 年创建了图形化的 ViolaWWW 浏览器,并且将自己的样式表语言集成到了浏览器中,他期望将语言变成 Web 的正式标准。虽然最终没有实现,但是为其后的样式表规范提供了一些启发。
ViolaWWW 发布的同时,Andreessen 在他自己的浏览器 Netscape Navigator 中采用了不同的样式实现方法。他没有创建专门用于网站样式的修饰语言,而是仅扩展了 HTML 标签元素及其属性。不幸的是,这种方式似乎使得 HTML 的标签变得极其复杂,看起来像这样:
This would be some font broken up into columns
一时间百家争鸣,各种技术方案应运而出,类似 RRP(一种支持缩写和简洁的样式表语言)或 PSL96(一种实际上允许使用函数和条件语句的语言)一样。这其中脱颖而出的想法是 HåkonWium Lie 于 1994 年 10 月首次提出的,它被称为层叠样式表或 CSS。
1.3 css 出世
CSS 之所以脱颖而出,是因为它很简单,特别是与某些最早的竞争对手相比。CSS 遵循一种可预见的,宽容的格式,几乎任何人都可以使用它。但是,CSS 的比较特殊的是它允许样式层叠,层叠意味着样式可以继承并覆盖先前已声明的其他样式,同时它又允许在同一页面上使用多个样式表。
Lie 认为程序员和设计师都将在单独的样式表中定义样式,浏览器可以充当两者之间的一种中介,并协商差异以呈现页面。css 刚刚推出时,业界具有很多争议,争议点就在于刚刚提到的宽松性。
不久后,Lie 找到了另外一个合伙人 Bert Bos,Bos 创建了自己的 Argo 浏览器,并在此过程中创建了他自己的样式表语言,该样式表语言最终进入了 CSS。两者开始制定更详细的规范,最终向 W3C 新建的 HTML 工作组寻求帮助。花费了几年时间,到 1996 年底第一版 css 定稿。
1.4 应用到浏览器
在 CSS 还只是一个草案的时候,网景的浏览器已经支持了上述提到的具有复杂属性标签的 HTML 元素,如 multicol,layer 和可怕的 blink 标签;另一方面,微软的 Internet Explorer 已经采用了一些 CSS 零碎的方式。但是他们的支持参差不齐,有时甚至是错误的。在最初的 CSS 版本推出五年后,还没有完全支持 CSS 的浏览器。
事情的转机在 TantekÇelik 1997 年加入 Internet Explorer 之后。他成为了渲染引擎的首席开发人员。从 2000 年开发第 5 版开始,Çelik 和他的团队将重点放在 CSS 支持上。在此期间,Çelik 经常使用他们的浏览器与 W3C 成员和 Web 设计人员交谈,以确保它们正确无误。最终,在 2002 年 3 月,他们交付了用于 Internet Explorer 5。第一个完全支持 CSS Level 1 的浏览器。
2. 原理
2.1 浏览器渲染
说到 css 的原理,就不能不提浏览器的渲染机制:
上图可以看出,浏览器渲染可以分两部分:
- HTML parser 生成 DOM 树;
- css parser 生成 style rules
最后,dom 树和 style rules 生成新的 render tree 渲染树,然后绘制到浏览器中,展示出来。
2.2 css 解析器
上边提到浏览器的渲染机制,可以看到 CSS 模块负责 CSS 脚本解析,并为每个 Element 计算出样式。Webkit 使用了自动代码生成工具生成了相应的代码,也就是说词法分析和语法分析这部分代码是自动生成的。这期间经历了以下几个步骤:
- 通过调用 CSSStyleSheet 的 parseString 函数,将上述 CSS 解析过程启动,解析完一遍后,把 Rule 都存储在对应的 CSSStyleSheet 对象中;
- 由于目前规则依然是不易于处理的,还需要将之转换成 CSSRuleSet。也就是将所有的纯样式规则存储在对应的集合当中,这种集合的抽象就是 CSSRuleSet;
- CSSRuleSet 提供了一个 addRulesFromSheet 方法,能将 CSSStyleSheet 中的 rule 转换为 CSSRuleSet 中的 rule ;
- 基于这些个 CSSRuleSet 来决定每个页面中的元素的样式;
3. 优先、继承原则
css 的解析遵循 2 个原则,优先原则和继承原则。
-
优先原则:后解析的内容会覆盖前边解析的内容;
- 同一个选择器从上到下读取 css 样式;
- 同一类选择器从上往下执行;
- 不同类型的选择器优先级遵循先执行低优先级再执行高优先级;
- 外部样式与内部样式合并之后一起执行,遵循从上往下的执行顺序;
- 内联样式最后执行 - 优先级最高;
- 加!important 标识最后执行。
-
继承原则 - 嵌套的标签拥有外部标签的部分样式,子元素继承父元素的样式。
- 文本相关的样式被继承,其他的不能继承;
- 块元素的宽度默认继承父元素的宽度,高度则自适应。
4. 浏览器兼容性
4.1 原因
兼容性问题是网站技术中老生常谈的问题,包括 HTML、JavaScript、CSS 都会出现兼容性问题。导致这个问题的根本原因是不同的浏览器厂商的内核不同,导致对 CSS 的解析效果不一致,继而显示效果千差万别。这里不去过分讨论内核不兼容的深层次原理,而是讨论一下大的解决思路,主要包括 4 个方面,浏览器 CSS 样式初始化、浏览器私有属性,CSS hack 语法和第三方插件。
4.2 定义默认样式
由于浏览器的 CSS 默认的样式都不一致,所以简单有效的方式是手动定义其默认样式。定义默认样式主要针对父元素,因为子元素会继承父元素的样式,直接定义父元素的样式简单便捷,例如:
*{
margin: 5px;/*外间距*/
padding: 5px;/*内间距*/
}
html {
line-height: 150%;/**/
-webkit-text-size-adjust: 100%;
}
body {
margin: 10px;/*外间距*/
}
a {
background-color: transparent;/*背景色*/
}
img {
border-style: none; /*边框*/
}
以上代码针对全局的内外间距、HTML 元素的行高、body 元素的外间距、链接元素的背景色、图片元素的边框样式做同一定义。
4.3 浏览器特有样式
有些浏览器的内核有自己特有的 CSS 样式,这种样式只能被自己识别,利用这种特性可以定义一些 CSS 样式用于兼容特有的浏览器版本,例如:
-ms-transform:rotate(-3deg); /*只兼容IE*/
-o-transform:rotate(-3deg); /*只兼容Opera*/
-webkit-transform:rotate(-3deg); /*只兼容Chrome/Safari*/
-moz-transform:rotate(-3deg); /*只兼容Firefox*/
4.4 CSS Hack
Hack 类似于上边提到的特有 CSS 样式,区别在于 Hack 是针对标准的 CSS 加上特殊符号,用于被特有的浏览器识别,以下举例:
a {
_margin: 10px; /*前缀加 "_" 只能被 IE6 及以下版本 IE 识别*/
*margin: 15px; /*前缀加 "*" , 只能被 IE7 识别*/
margin: 12px\9; /*值后缀加 "\9" 被 IE6 及以上版本 IE 识别*/
margin: 16px\0;/*值后缀加 "\0" 被 IE8以上版本的IE 以及 Opera15 以下版本识别 */
}
以上举了几个代表性的 Hack 的例子,实际上花大力气解决这些兼容性问题, 无非是给各个浏览器厂商填坑罢了,开发项目中一般不推荐使用过多的 Hack ,因为这会导致代码生僻难懂,维护成本过高。
4.5 第三方库
引入第三方库可以解决一些兼容性问题,方便快捷,成本较低,使用时只需要通过引入其 CSS 库文件即可,不过具体是否能解决百分之百的兼容性问题还需要具体使用测试。以下列举一些第三库:
4.5.1 respond.js
有些浏览器不支持 CSS 媒体查询,要实现响应式设计难度较大,这个库可以帮助解决不兼容媒体查询,具体使用方式只要引用这个 JavaScript 库即可,它的 GitHub 地址是 respond.js
4.5.2 rem.js
在 css3 中引入了字体单位 rem,这是一个相对单位,使用较为频繁,但是一些老的浏览器不支持,所以需要引入第三方库 rem.js,使用时只要引入 JavaScript 库文件即可。
4.5.3 normalize.css
由于不同的浏览器对于一些 CSS 的默认样式是不相同的,但是有时我们为了兼容性需要它们的默认展现形式一致,所以简单的方案是引入一个 CSS 库,它的地址是 normalize.css。
5. 小结
本章通过介绍 CSS 的历史来源,让大家了解了 CSS 的发展历程,然后简单说明了 CSS 在浏览器中渲染视觉样式的原理,最后提到了 CSS 在不同浏览器的兼容性情况及其解决方案。