《WebKit 技术内幕》之六(1):CSS解释器和样式布局
????????CSS解释器和规则匹配处于DOM树建立之后,RenderObject树之前,CSS解释器解释后的结果会保存起来,然后RenderObject树基于该结果来进行规范匹配和布局计算。当网页有用户交互或者动画等动作的时候,通过CSSOM等技术,JavaScript代码同样可以非常方便地修改CSS代码,WebKit此时需要重新解释样式并重复以上这个过程。
1 CSS基本功能
????????先谈一谈HTML网页的开发者们所遭遇的痛苦和悲惨的经历。在CSS出现之前或者更早,HTML网页设计者们因为要设计不同风格和样式的元素,所以规范不停地加入很多新的元素来表示网页布局,例如p、span等元素。然而,问题依然存在,例如,使用表格(Table)元素来排列网页中的元素,这可能存在一些问题:其一,表格经常内嵌表格,导致网页内容较大,占用带宽;其二,被搜索引擎解析后,网页内容将会变得杂乱无章。所以这时候急需一种技术来解决这些问题。庆幸的是,此时CSS出现了。
????????CSS的全称是Cascading Style Sheet,中文名是级联样式表,主要是用来控制网页的显示风格。它被广泛地使用在网页中,绝大多数的现代浏览器都支持它。CSS的一个比较重要的特征就是将网页的内容和内容的展示方式分离,这对开发者提高开发效率非常有用。另一个重要的特征是它很强大,而且不是一般的强大,特别是新的CSS3标准,不仅能提供对页面任意元素的精准控制,同时还能提供丰富多彩的样式。简而言之,CSS是一种非常出色的文本展示语言。
????????Web开发者有两种方法可以使用CSS,第一种就是示例代码中将CSS的代码放入元素“style”中,这称为内部样式表;第二种就是形如代码<link rel="stylesheet"type="text/css"href="css-url.css">这样的用法,引用了一个外部的CSS文档,这称为外部样式表。
示例代码? 6-1 使用CSS的HTML网页
????????为了便于读者对CSS有个直观的印象,示例代码6-1展示了一个使用CSS的简单例子,后面很多描述都是基于它来展开的。不过,该例子虽然简单,但却是一个展示了CSS众多特征的例子,CSS的主要部分包含在元素“Style”中,也就是例子中的第3行到第16行。同时,JavaScript代码部分也有对样式的操作,如第29行,后面的部分会对它们逐一加以解释。
????????样式的来源有三种类型,其一是网页开发者编写的样式信息,它被包含在网页或者外部样式文件中,这也是最常见的方式;其二是网页的读者设置的样式信息,读者可以设置一个样式,这个样式可以应用到其浏览的网页;其三是浏览器的内在默认样式。以上三种类型的优先级自然是递减的。
????????CSS语言主要定义了一系列作用在各个元素上的样式规则,如示例代码6-1中的第4到17行就是一个规则,该规则表示div所应用的部分样式设置。CSS3标准中还有很多新的功能,示例代码6-1中也描述了其中的一些功能,那就是3D变形(Transform)。这些样式给网页设计带来了非常震撼的视觉效果,读者可以尝试在浏览器中运行上面的网页。这些新功能笔者将在高级篇中的第8章来介绍。
????????CSS2引入了一个概念,可以设置跟“media”相关的样式信息,例如用于屏幕显示、打印等。这样,网页可以为了达到不同的目的来设置CSS样式信息,其格式形如“@media screen{div{color:read}}”,它表明该CSS设置的属性仅仅作用于屏幕的显示。
????????样式规则是CSS规范中最基本的组成,通常,CSS文档包含一系列的样式规则,如上所述,示例代码6-1中的第4行到第17行就是一个完整的样式规则。
? ? ? ?下图描述了一个典型的CSS规则结构。一个规则包括两个部分——规则头和规则体。规则头由一个或者多个选择器组成,选择器随后会被介绍;规则体则由一个或者多个样式声明组成,每个样式声明由样式名和样式值构成,表示这个规则对哪些样式进行了规定和设置。
? ? ? ? ? ? ? ? ? ? ? ? ?CSS的样式规则表示
????????当HTML中的某个元素经过后面的匹配算法使用了这条规则,那么就将这些样式设置成该元素的样式,除非有更高优先级的规则匹配上该元素。
????????CSS的选择器是一组模式,用来匹配相应的HTML元素。当选择器匹配相应元素的时候,该选择器包含的各种样式值就会作用于匹配的元素上。通过选择器,CSS能够精准地控制HTML页面中的任意一个或者多个元素的样式属性。查看示例代码6-1中的第4行,该行中的“div”就是一个选择器,这是元素选择器类型,其含义是选择该页面中的所有“div”元素。因为仅有第20行包含一个“div”元素,所以,该元素会使用该选择器的属性设置。那么,“div”元素下面所设置的样式等属性(花括号内)都会作用于该元素,从第6行到第16行。
????????示例代码中的选择器仅是众多选择器类型中的一种,从CSS1到CSS3,规范陆续地加入了多达42种选择器,极大地增强了选择的能力,下面介绍其中一些主要的选择器。
????????还有很多其他类型的选择器,例如伪类选择器、通用选择器、群组选择器、根选择器等,这里不再一一介绍,请查阅CSS规范。
????????介绍完选择器之后,还有个非常重要的问题,那就是优先级。对于某个元素的一个属性,因为多个选择器可能都作用于该元素,并且它们可能对该属性设置了不同的属性值,这种情况下,应该怎么确定使用哪种选择器呢?
????????一般而言,选择器描述得越具体,它的优先级越高,也就是说选择器指向的越准确,它的优先级就越高。例如,如果用1表示标签选择器的优先级,那么类选择器优先级是10,ID选择器就是100,数值越大表示优先级越高。所以,尽量使用控制精确的选择器,使用优先级合理的选择器。假如对于元素的某一样式属性,两个匹配上的选择器都设置了该属性值,那么在此情况下,优先级高的选择器所设置的属性值将会应用到该元素上。
????????标准中还引入了两个新的JavaScript接口:QuerySelector和QuerySelectorAll。这两个接口让CSS定义的所有选择器都可以作为参数传给这两个接口,从而获取到相应的HTML页面中的DOM节点。Chrome、Safari和Firefox等浏览器都支持该接口。
????????框模型(Box model,或称箱子模型)是CSS标准中引入来表示HTML标签元素的布局结构。一个框模型大致包括了四个部分,它们从外到内分别是外边距(Margin)、边框(Border)、内边距(Padding)和内容(Content)。图描述的就是一个标准的框模型结构。在HTML网页中,每个可视元素(之所以强调可视是因为很多HTML元素其实不是用来显示的,例如用来表示语义的元素)的布局都是按照框模型来设计的。网页通过对元素设置这些样式属性,就可以达到特定的布局效果。
????????框模型中的最边缘部分分别是四个方向上的外边距(TM、RM、BM、LM),可以为这四个外边距设置不同的大小,图中特意将这些方向上的外边距绘制得不一样,也是表明了这个含义。图中外边距往内是四个方向上的边框(左边框、右边框、上边框、下边框),再次是四个方向上的内边距(同边框类似),最后是该元素显示自己的内容所使用的区域。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CSS标准的框模型结构
????????示例代码也包含了框模型的使用方法,但是不足以完全展示这一模型。所以笔者将它略作修改,变成示例代码所示的代码。笔者希望通过专门的框模型示例和显示结果来帮助读者更直观清晰地理解该模型。
示例代码? ? ?框模型的HTML网页代码片段
CSS代码:
#adiv/*ID选择器*/ { width:300px;/*宽度*/ background-color:#efefef;/*背景色*/ border:2px solid black;/*边框*/ } .aclass/*类选择器*/ { border:5px solid red;/*边框*/ margin:150px 100px 10px 40px;/*外边距*/ padding:15px 20px 25px 30px;/*内边距*/ color:green;/*颜色*/ }
HTML代码:
<p> This is a test to demonstrate the mechanism of layout!</p> <div id="adiv"> <div class="aclass">A B C D E F G H I J K L M N O P Q RS T</div> </div>
????????图是示例代码在Chrome浏览器中的显示结果。ID为“#adiv”的div元素被设置了边框是为了让读者了解它的内容区域。图中最大的区域就是该div元素的内容区域,最外边的黑色边框就是它的边框。该元素的内部就是“.aclass”的类选择器所选择的div元素的包含块,笔者后面会介绍包含块的概念。包括块内部就是类型为“.aclass”的div元素的框模型显示结果,这也是笔者希望描述的框模型结构。为了便于读者对框模型的理解,在显示区域旁边的标注表明了框模型的各个属性值,可以同图的框模型进行对照,看看实际的效果是怎么样的。
????????????????????????????????????????图示例代码的框模型显示结果
????????????????框模型是布局计算的基础,渲染引擎可以根据模型来理解该如何排版元素以及元素之间的位置关系。
????????当WebKit计算元素的箱子的位置和大小时,WebKit需要计算该元素和另外一个矩形区域的相对位置,这个矩形区域称为该元素的包含块。上面介绍的框模型就是在包含块内计算和确定各个元素的,包含块的具体定义如下。
????????结合实例来讲,类型为“aclass”的div元素的包含块就是其父亲的内容区域,其框模型就是在该内容区域上进行计算生成得来的。
????????CSS标准中定义了各式各样的样式属性,用来描述元素的显示效果。示例代码6-1的第6行到第16行设置了选择的元素的样式属性值,笔者大致把这些属性分成以下类别。
????????想象一下上面两个示例代码中关于CSS部分的代码,读者会发现它们都是静态的,那么CSS有没有提供一些方法可以让开发者自定义一些脚本去操作它们的状态呢?这就是CSSOM,称为CSS对象模型。它的思想是在DOM中的一些节点接口中,加入获取和操作CSS属性或者接口的JavaScript接口,因而JavaScript可以动态操作CSS样式。DOM提供了接口让JavaScript修改HTML文档,同理,CSSOM提供了接口让JavaScript获得和修改CSS代码设置的样式信息,这听起来非常酷吧,确实是这样的。
????????对于内部和外部样式表,CSSOM定义了样式表的接口,称为“CSSStyleSheet”,这是一个可以在JavaScript代码中访问的接口。借助于该接口,开发者可以在JavaScript中获取样式表的各种信息,例如CSS的“href”、样式表类型“type”、规则信息“cssRules”等,甚至可以获取样式表中的CSS规则列表。这个接口同DOM中的“Script”节点或者“Link”节点不一样,它是CSSOM定义的新接口。开发者可以通过document.stylesheets查看当前网页中包含的所有CSS样式表,这是因为CSSOM对DOM中的Document接口进行了扩展,下面是新加入的属性。
partial interface Document{ readonly attribute StyleSheetList styleSheets; attribute DOMString? selectedStyleSheetSet; readonly attribute DOMString? lastStyleSheetSet; readonly attribute DOMString? preferredStyleSheetSet; readonly attribute DOMString[] styleSheetSets; void enableStyleSheetsForSet(DOMString? name); };
????????通过上面这些属性,开发者甚至可以动态选择使用哪些CSS样式表。获取的样式表就是前面定义的CSSStyleSheet对象,JavaScript代码可以修改这些对象的属性,非常便于使用。
????????W3C还定义了另外一个规范,那就是CSSOM View,它的基本含义是增加一些新的属性到Window、Document、Element、HTMLElement和MouseEvent等接口,这些CSS的属性能够让JavaScript获取视图信息,用于表示跟视图相关的特征,例如窗口大小、网页滚动位移、元素的框位置、鼠标事件的坐标等信息。这些特征在很多浏览器中获得了支持,它们非常有用,下面以CSSOM View对Window的扩展为例,笔者省略了一些属性。
partial interface Window{ MediaQueryList matchMedia(DOMString media_query_list); readonly attribute Screen screen; //viewport readonly attribute long innerWidth; readonly attribute long innerHeight; //viewport scrolling readonly attribute long scrollX; readonly attribute long pageXOffset; readonly attribute long scrollY; readonly attribute long pageYOffset; … };
????????本节中,笔者希望通过例子来理解CSSOM标准定义的内容,结合选择器的匹配方法来加深对CSS技术的认识。
????????图是一个代码示例图和在Chrome浏览器的控制台中的信息。下面按照步骤逐步分析这一例子。
图理解CSSOM和选择器的示例代码和控制台语句
????????还可以在控制台中尝试一下其他的JavaScript语句,或者在更复杂一些的网页里面理解规则,这里只是描述基本原理。