提到 CSS,很多 Web 开发人员都不陌生。它是在 Web 应用中控制展现的标准技术。CSS 与 HTML 和 JavaScript 一起,构成了 Ajax 应用的基础。对于 CSS,已经有很多图书和文章进行过详细的介绍。本文不介绍 CSS 语法的细节,而是从一些实际开发中会遇到的问题出发,讨论一些与具体实践相关的话题。
下面首先介绍 CSS 中的一些重要概念,可以帮助读者加深对 CSS 的理解。本文中以 CSS 2.1 规范来进行说明。
CSS 的重要概念
CSS 的语法非常简单,包含的元素也很少,主要有“@ 规则”和样式规则集。“@ 规则”是以@
开头的规则声明,常用的有@import
、@media
和@charset
等。样式规则集是一系列样式声明规则的集合。每个样式规则集由选择器和声明两部分组成。
声明是 CSS 中样式属性的名值对,其形式是“属性名称 : 属性值
”。如声明“font-color : red
”把样式font-color
的值设为red
。
属性继承
对于 CSS 中的某些样式属性来说,如果元素没有显式的指定一个值,该属性就会继承该元素的父元素的这个属性的值。常见的会被继承的属性有:visibility
、color
、font
和text-decoration
等。需要注意的是,发生属性继承的时候,子元素继承的是父元素属性的计算值,而不是指定值。比较典型的例子是通过em
或是百分数指定的字体大小。比如父元素的字体大小的声明是“font-size : 1.2em
”,而实际计算出来的字体大小是 12px,则子元素继承的是 12px,而不是 1.2em。通过设置属性的值为inherit
可以让该属性强制继承其父元素对应属性的值。
在介绍完 CSS 的重要概念之后,下面介绍 CSS 规则的层叠顺序。
CSS 规则的层叠顺序
正如 CSS 的全称“层叠式样式表(Cascading Style Sheets)”所表示的含义一样,CSS 中的样式声明规则是有层叠顺序的。可以在不同的地方把相同的规则应用在相同的元素上面。比如对某个 P 元素,某条 CSS 规则将其文本颜色设为红色,而另外一个规则把其文本颜色设为蓝色。显然这两条规则是互相冲突的。在具体显示的时候,浏览器会根据层叠顺序来判断应用哪条规则,从而确定该 P 元素的文本颜色。层叠顺序的基本原则很简单,那就是越具体的规则,优先级越高。浏览器根据优先级高低来确定应用哪条规则。具体的来说,一条规则的优先级与其使用的选择器和所在的位置相关。下面分别进行说明。
根据所使用选择器的优先级顺序
在判断一条规则的优先级时,会首先判断其所用的选择器的优先级顺序。按照优先级从高到低的顺序排列如下:
- 使用了
!important
声明的规则。 - 内嵌在 HTML 元素的
style
属性里面的声明。 - 使用了 ID 选择器的规则。
- 使用了类选择器、属性选择器、伪元素和伪类选择器的规则。
- 使用了元素选择器的规则。
- 只包含一个通用选择器的规则。
当浏览器需要判断应用互相冲突的规则中的哪条规则的时候,首先会根据上面的顺序来判断优先级。优先级高的规则就会被应用。如果优先级相同,则需要进一步判断所使用的选择器的个数。选择器数目较多的规则优先级较高。比如下面两条规则:#myDiv .header .main {}
和#myDiv .header span {}
,前者的优先级更高。因为前者有一个 ID 选择器,两个类选择器;而后者也有一个 ID 选择器,但是只有一个类选择器。上述的优先级判断算法类似于一般的数字大小比较,首先比较最高位,如果相同的话,再比较次高位,以此类推。
如果按照上面的优先级顺序来判断,规则的优先级相同的话(即所用的选择器类别相同,同类选择器的数量也相同),则需要进一步根据规则所在的位置来判断。
根据规则所在位置的优先级顺序
对于规则所在的不同位置,可以按照优先级从高到低排列如下:
- 在 HTML 文档的
head
元素中的style
元素中定义的规则。 - 通过
style
元素中的@import
语句引入的样式表中定义的规则。 - 通过
link
元素引入的样式表中定义的规则。 - 在
link
元素引入的样式表中,再通过@import
语句引入的样式表中定义的规则。 - 最终用户提供的样式表中定义的规则。
- 浏览器默认提供的规则。
如果规则所在的位置相同的话,出现在样式表后面的规则的优先级更高。
通过这样的两套规则机制,就保证了浏览器可以解决互相冲突的规则的优先级问题。下面通过一个示例来具体说明层叠顺序的机制。
层叠顺序示例
演示层叠顺序的示例所使用的 HTML 和 CSS 如代码清单 1所示。
清单 1. 层叠顺序示例
<style> * { color : black; } p { color : gray; } .p_red{ color : red; } p.p_blue { color : red; } #p1 { color : green; } .p_blue { color : blue; } .p_blue2 { color : blue !important;} </style> <body> <p id="p1" class="p_red"> 示例文本 1</p> <p class="p_red"> 示例文本 2</p> <p class="p_blue"> 示例文本 3</p> <p class="p_blue p_blue2"> 示例文本 4</p> </body>
如代码清单 1所示,HTML 文档中定义了 4 个 P 元素以及一些 CSS 规则用来定义其文本颜色。对于第一个 P 元素来说,可以应用在之上的有两条规则:一条是通过 ID 选择器定义的,另外一条是通过类选择器定义的。由于 ID 选择器优先级高,第一条规则被应用,所以文本颜色是绿色;第二个 P 元素只能应用一个类选择器,所以文本颜色是红色;第二个 P 元素有两个规则可以被应用。规则p.p_blue
比.p_blue
多一个元素选择器,前者被应用,所以文本颜色是红色;最后一个 P 元素所能应用的规则中,p_blue2
使用了!important
声明,优先级最高,因此文本颜色是蓝色。
对于上面提到的两套规则机制,一个重要的例外就是用户样式表中的包含的带!important
的声明具有最高的优先级,超过网站本身提供的样式表中带!important
声明的规则。这样设计的意图是允许用户使用自己提供的样式来覆盖网站提供的样式,以提高网站的可访问性。
熟悉了 CSS 规则的层叠顺序之后,就可以解决常见的 CSS 样式被覆写的问题。很多时候,尤其在团队开发中,一个 CSS 文件可能被多个开发人员修改。经常会遇到的一个问题是,自己的 CSS 样式没有起作用。通过 Firebug 可以发现,原因是自己的 CSS 样式被其他人写的样式给覆写了。根本原因是自己样式声明中使用的选择器的优先级较低。解决这个问题的办法就是查看与你的样式发生冲突的 CSS 规则所使用的选择器,并相应提升自己的选择器的优先级,防止自己的样式被覆写。
在介绍完 CSS 规则的层叠顺序之后,下面介绍 CSS 布局中重要的概念:盒模型。
盒模型
盒模型(Box model)是 CSS 中进行页面布局的重要概念。页面上文档树中的每个元素在显示的时候,都会对应生成一到多个矩形盒子。布局是根据这些盒子来进行的。盒子之间可以互相嵌套。对于一个元素来说,其产生的盒子如图 1所示。
图 1. CSS 的盒模型示意图
从图 1中可以看到,一个盒子从里到外有四个层次:最里面的是内容区域,包含元素本身的内容;紧接着是填充区域;然后是边框;最后是空白区域。内容区域的宽度和高度的确定比较复杂,需要综合考虑样式width
和height
的值,以及该盒子中包含的其它盒子的类型。填充区域、边框和空白区域的上、下、左、右四个方位的宽度都可以通过样式padding
、border
和margin
来分别指定。通过样式background
指定的背景颜色或图片可以应用在内容区域、填充区域和边框上。空白区域总是透明的,不会应用背景样式。
在介绍完盒模型之后,下面介绍如何对页面上的元素进行布局和定位。
布局和定位
浏览器在渲染 HTML 页面的时候,所进行的操作就是把上面提到的文档树上的元素所对应的盒子,按照一定的规则进行排列。下面将详细介绍布局的规则以及元素是如何在页面中定位的。
盒子类型
不同的盒子类型在布局时的行为不一样。盒子的类型是由生成它的元素的类型来决定的。通过样式display
可以指定元素的类型。该样式可以使用的值很多,主要的有block
、inline
、inline-block
和none
等。样式display
的值为block
的元素称为块级元素(block-level element),它们所生成的盒子是块盒子;inline
或inline-block
的元素称为行内元素(inline-level element),它们所生成的是行内盒子。块盒子与行内盒子的具体说明如下:
包围块
在对页面上的元素进行定位的时候,包围块(containing block)是个很重要的概念。很多时候都需要根据其包围块来确定某元素对应的盒子的位置和大小。包围块的确定并不复杂:对于文档根元素,其包围块是浏览器的窗口;如果元素的样式position
的值是static
或relative
,则其包围块是最近的块级祖先元素的内容区域;如果样式fixed
,则其包围块是浏览器的当前视口(viewport);如果样式absolute
,则首先在祖先元素中寻找样式absolute
、relative
或fixed
的元素。找到这个元素之后,如果该元素是行内元素,则包围块由所生成的盒子的填充边界来确定,如果是块级元素,则包围块是该元素的填充区域。
元素的定位由样式position
的值来确定,该样式的默认值是static
,其它可选的值有relative
和fixed
。样式position
的值为static
的值称为非定位元素,其它的称为定位元素。
在 CSS 中,一个盒子可能有三种定位方式:正常文档流、浮动和绝对定位。通过样式position
和float
的值可以控制所采用的定位方式。
正常文档流
正常文档流是按照盒子的类型来定位的。每个盒子都会属于某个格式化上下文中,块或是行内的。块格式化上下文会按照从上到下的垂直顺序来排列盒子。行内格式化上下文则按照水平顺序来排列盒子,从左到右还是从右到左取决于文档的顺序。
通过将样式position
的值设为relative
,可以把当前元素的盒子设成相对定位的。相对定位的盒子首先按照正常文档流来排列,然后再根据其样式left
、right
、top
和bottom
的值来相对原始位置进行偏移。在它之后出现的盒子不受偏移的影响,就好像它还在原始位置一样。相对定位盒子的一个常见的作用是作为其绝对定位的子盒子的包围块。
浮动定位
通过将元素的样式float
的值设为left
或right
,可以使得该元素对应的盒子在当前行上向左或向右浮动。浮动的盒子会尽可能的向左或向右移动,直到接触到包围块或是另外一个浮动盒子的边界。如果当前行没有足够的空间给浮动盒子,该盒子会往下移动直到有足够的空间为止。
浮动的盒子不属于正常文档流的一部分。在浮动盒子之前和之后的非定位块盒子按照其在正常流中的位置排列,相当于浮动盒子不存在一样。而浮动盒子之后的行内盒子则会被缩短,紧跟在浮动盒子的后面。所形成的效果就是后面的行内盒子包围着浮动的盒子。在网页中文字环绕图片的效果就可以通过这种方式来实现。通过样式clear
可以强制盒子不跟在浮动盒子的后面。
绝对定位
将样式absolute
或fixed
,使得元素对应的盒子变成绝对定位。绝对定位的盒子被从正常文档流中完全移除,对相邻的盒子没有任何影响。绝对定位的盒子的位置是相对其包围块来确定的。具体的位置由样式bottom
来确定。
在介绍完 CSS 中的布局和定位之后,下面介绍如何解决不同浏览器之间 CSS 样式的兼容性问题。
浏览器兼容性
在使用 CSS 为网页添加样式的时候,不得不面对的一个棘手的问题就是浏览器的兼容性所带来的复杂性。不同的浏览器对于 CSS 规范本身的理解,以及具体的实现都有很大不同。浏览器本身还可能存在各种各样的 bug。在 Firefox 上面显示正常的网页,用 IE 打开的话就可能发现整体的布局错位。相同浏览器的不同版本之间的差别也会很大。开发人员需要花费大量的时间和精力来保证网页在各种浏览器上呈现的样式是一致的。一般来说,解决浏览器兼容性问题有三种做法:利用浏览器本身的支持、使用招数和使用 JavaScript。下面先介绍浏览器本身的支持。
@H_627_301@ 浏览器本身的支持可以利用浏览器本身支持的能力,在一定程度上解决兼容性的问题。比较典型的是 IE 提供的条件注释。通过条件注释,可以在页面由特定版本的 IE 来显示的时候,设置特殊的样式,如代码清单 2所示。
清单 2. IE 提供的条件注释
@H_404_307@ <!--[if IE 6]> <style type="text/css"> span { color : red; } </style> <link rel="stylesheet" href="ie6.css" type="text/css"> <![endif]--> 代码清单 2中条件注释中包含的样式和引入的 CSS 文件ie6.css
,只对 IE 6 才起作用。下面介绍招数。
招数
招数(hack)是利用浏览器本身对 CSS 规范支持的不完善或是实现上的 bug,来针对特定的浏览器应用样式的一些做法。这些招数通常用来满足这样的需求,即对某个或某些浏览器应用特定的样式。IE 6 不支持直接后代选择器,即不识别div > span
这样的选择器。如果想实现这样的效果:IE 6 上面的某个span
元素的左空白边界的宽度设为 20 像素,而其它浏览器的设为 10 像素,可以利用上面提到的招数来实现,具体如代码清单 3所示。