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 的原理,就不能不提浏览器的渲染机制:
base64str
上图可以看出,浏览器渲染可以分两部分:

  • 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 默认的样式都不一致,所以简单有效的方式是手动定义其默认样式。定义默认样式主要针对父元素,因为子元素会继承父元素的样式,直接定义父元素的样式简单便捷,例如:

实例演示
预览 复制