个人工具

在AOM2.0中使用CSS样式表来定制页面


1. 概述

虽然AOM提供了一套统一的默认风格来展示页面,并且也提供了好几套候选皮肤供用户选择,但显然,这些展现风格是无法满足复杂多变的应用场景的。而在实际应用中,用户往往希望基于某一套皮肤之上进行一些简单的细节扩展,而不想重新去定义整套皮肤。本节将介绍在AOM中使用独立的CSS样式表对展现效果进行定制的一些技巧与方法。

2. 引入自定义的CSS风格

AOM2.0的最终渲染结果使用CSS层叠样式表来定义展现效果。通过修改或扩展这些CSS定义,我们可以对展现效果进行简单快捷的微调。

观察任意一个AOM2.0渲染页面的源代码,我们可以在<head>部分看到类似这样的代码:

<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/skin/default/yuiext/css/ext-all.css" _fcksavedurl="/ear1/_global/skin/default/yuiext/css/ext-all.css"/>
<link class="x-skin" rel="stylesheet" type="text/css" href="/ear1/_global/skin/default/yuiext/css/ext-extra.css"/>

这些代码引入了AOM2.0默认皮肤的风格定义。应该注意的是,这里引入的是应用部署后创建的样式表副本。原始文件在operamasks-impl.jar的

org\operamasks\faces\skin\default\yuiext\css\

目录下。通过观察这些皮肤文件,可以对AOM中CSS的使用有一个整体印象(例如命名规则,默认标签风格等)。但由于这些样式表文件是AOM发行包的一部分,所以用户不应该直接修改这些文件,否则随着AOM版本的更新,用户所作的修改会被覆盖。

因此,用户应该通过在xhtml中引入自定义的CSS样式定义来覆盖默认的皮肤样式,来实现对页面展现风格进行微调。在AOM中引入自定义风格定义的方法与普通html类似,可以直接在xhtml页面中使用<style>标签进行定义,也可以在<head>部分使用<link>标签引入自定义的外部CSS文件:

<w:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="/articles/custom-style/html_single/mystyle.cs" />
</w:head>

AOM会将用户加入的<link>标签放在引入默认风格的<link>标签之后,从而保证用户引入的风格会覆盖默认风格。

3. 获取CSS class

使用CSS对页面展示进行控制的基本流程是,为页面元素指定id(注意并不是AOM组件的id属性,而是最终渲染结果中的元素id)或class属性,然后针对此id或class定义对应的CSS样式。AOM在渲染页面时,已经为页面中的关键元素指定了默认的class,因此只要能获取到元素的class属性,就可以使用CSS对其展现效果进行定制。

由于AOM借助了EXT2.0组件库来渲染最终页面,而EXT组件库在渲染过程会对浏览器的DOM树结构进行大量动态修改,直接观察响应页面的源代码,往往无法得知页面元素的实际状态。因此,我们需要借助一些浏览器DOM树的分析工具来获取页面元素的class属性值甚至对CSS样式进行简单调试。此类工具主要有FireFox的插件FireBug,和Internet Explorer的插件IE Developer Toolbar。

3.1. 使用FireFox与FireBug插件调试CSS样式

这里就不对FireFox与FireBug插件本身作过多的描述,仅以一个简单例子来说明如何利用它们对AOM渲染的页面进行定制。我们的需求是,修改DataGrid组件的展现风格:

修改前:

修改后:

首先,我们来修改表头部分的样式。使用FireBug的Inspect(监测)功能:

选中表头的任意一个单元格

现在在FireBug中可以看到这个元素在DOM树中的实际状态,以及影响它的CSS样式定义:

观察页面元素树,我们可以发现以下几点:

  1. DataGrid的表头部分被单独渲染为一个cellspacing="0" (单元格间无间隔) cellpadding="0" (单元格无默认内边距) border="0" (不显示表格线)的table。我们看到的表格线,事实上是通过表格内部元素的样式来模拟的。

  2. 每个元素,例如td和其中的div,都被设定为多个class。其中包括一个动态生成的,按元素位置顺序编号的临时class,例如上例中的x-grid3-hd-1。在通用的皮肤文件中不会对此这些class作出定义,使用此class配合id可以对单个DataGrid内的元素进行精细控制。

  3. 此外,则是根据元素的逻辑功能为其指定class,例如上例,我们可以看出表头中的每个tr(对应表头一行)被设为x-grid3-hd-row,其中的td(对应一个单元格)被设为x-grid3-hd与x-grid3-hd-cell,单元格中的内容被设为x-grid3-hd-inner。这些class是被皮肤风格所使用,用于对整体风格进行控制的。我们在这里也主要是通过覆盖对这些class的风格定义,来达到调整页面风格的效果。

现在,我们先把表头的文本颜色设为白色。FireBug提供了非常方便的CSS定位与调试功能,在上图右边的Style窗口提供了以下信息:

  • 所有影响当前元素并且已作定义的class。注意,可能影响当前元素但未定义样式的class不会列出,例如上例中的x-grid3-hd-1;

  • 这些样式定义所属的文件(若为element.style则表示这是在元素上通过style属性硬编码的样式);

  • 样式定义之间的覆盖关系,被覆盖的样式定义以删除线方式显示;

  • 当前元素从容器元素(父元素)继承样式定义的层次关系(从当前元素开始倒序排列)。

设定风格的原则之一是在保证生效而又不产生副作用的前提下尽可能使用大范围的class,以便为细节的风格调整留出空间。在这里可以考虑把颜色定义放在"x-grid3-hd-row td"(对应class为x-grid3-hd-row的容器元素中的所有td元素)中。FireBug中提供了即时的CSS编辑预览功能,在Style窗口中找到"x-grid3-hd-row td"风格定义,添加一条属性"color : #fff;"

添加后:

可以看到,表头文本已经变成了白色。

现在来定制表头底色,显然原本的表头风格使用了渐变的背景,因此我们首先要找到对此进行定义的位置再作修改。考虑到背景定义要么在tr中,要么在table中或与它们同等级别的div中,很容易可以在class为x-grid3-header的div中找到背景风格定义:

 

在我们的需求中,我们不希望表头背景延伸到没有数据的空白区域中,因此,在这里删去background属性。并把背景色定义加入到x-grid3-hd-row中(注意,由于在默认皮肤中没有对x-grid3-hd-row的单独定义,为了预览需要我们可以先把风格定义加入到tr元素的element style中):

现在可以看到表头已经符合我们的需求了:

我们先把到目前为止的改动总结一下,加入到我们的自定义样式表文件中。注意自定义的样式表需要以CSS风格属性为单位覆盖原有的属性,因此上例中删去.x-grid3-header的background属性的动作,在自定义样式表中必须将background定义为我们需要的样式。

.x-grid3-hd-row td {
color : #FFFFFF;
font-weight : bold;
}

.x-grid3-header {
background : none;
}

.x-grid3-hd-row {
background-color : darkBlue;
}

同理,我们采用以下样式定义来定制被选中的行:

.x-grid3-row-selected {
background-color : #FFEEB0;
}

.x-grid3-body .x-grid3-row-selected .x-grid3-td-numberer,
.x-grid3-body .x-grid3-row-selected .x-grid3-td-checker,
.x-grid3-body .x-grid3-row-selected .x-grid3-td-expander {
background:#FFE481 none repeat scroll 0%;
border-right:thin solid #D0D0D0;
}

将以上内容放入工程中的mystyle.css文件中,在xhtml页面中的<head>部分加入:

<link rel="stylesheet" type="text/css" href="/articles/custom-style/html_single/mystyle.css" />

可以看到,新的定义已经生效了。但是,选中行的底色还是原来的蓝色:

这是因为,在默认皮肤(ext-all.css)中,.x-grid3-row-selected的background属性被加上了!important标志,具有更高的优先级。因此,我们需要将mystyle.css中的相关定义也加上!important标志:

.x-grid3-row-selected {
background-color : #FFEEB0 !important;
}

现在可以看到,表格的风格已经完全符合我们的需求了。

3.2. 使用IE Developer Toolbar在IE6下调试CSS样式

使用Microsoft提供的Internet Explorer Developer Toolbar可以在IE6下对DOM树与CSS样式进行类似的分析与调试。IE Developer Toolbar可以在以下网址下载:

http://www.microsoft.com/downloads/details.aspx?familyid=e59c3964-672d-4511-bb3e-2d5e1db91038&displaylang=en

分析过程与使用FireFox与FireBug类似,但是由于IE Developer Toolbar只列出影响当前元素的所有样式属性,而不能准确定位此属性被定义的位置(虽然有trace style功能,但此功能定位经常不准确),不列出被覆盖的父元素属性,也不提供直接修改CSS样式进行预览的功能。因此通常情况下,建议先利用FireFox与FireBug定出整体的修改方案,再对与IE不兼容的部分,使用IE Developer Toolbar在IE下进行调试。

使用IE Developer Toolbar的一些注意事项:

  1. 安装后,需要在IE的查看菜单,浏览器栏子菜单中打开IE Developer Toolbar。

  2. 应该在打开IE后,访问任何网页之前先打开IE Developer Toolbar,再访问要调试的网页,否则IE Developer Toolbar可能无法获取页面信息。

  3. IE Developer Toolbar中选取元素的按钮比较小而且没有命名,在其窗口的左上角:

  4. 默认只显示为本元素定义的风格属性,若希望显示从父元素继承的所有属性,应勾选Show Default Style Values选项。

4. 指定组件的CSS class

除了自动设定的CSS class外,组件还提供了属性来定义各种状态下的ass。假如我们现在需要让<w:textField>在失去焦点时显示如下样式:

而在获得焦点是显示如下样式:

我们可以这样编写页面:

<w:textField focusClass="x-form-field-focus" value = "1234" width="50" style="text-align : right"/>

其中focusClass属性指定当textField获得焦点时为其设定的CSS class,现在我们就可以编写如下样式表来实现上述效果:

.x-form-field {
border-top : 0px;
border-left : 0px;
border-right : 0px;
border-bottom : 2px dotted;
background: #FFFFFF none;
}

.x-form-field-focus {
border : 1px solid;
background:#FFFFFF url(_global/skin/default/yuiext/images/form/text-bg.gif) repeat-x;
}

更多关于CSS相关属性的信息,请参考组件属性文档。

5. 同一组件的不同风格

之前介绍的方法,主要是设置页面上组件的整体风格。实际应用中,我们往往需要为同一页面上的同类组件设置不同风格。这时可以使用Section 4, “指定组件的CSS class”中介绍的方法,为个别组件指定特定的CSS class。但由于ext组件在渲染过程中往往嵌套了多层div和table,使用组件属性指定的class只能放在具有最大影响力的某层元素中(我们当然不希望为某个类设置border样式后,发现组件被套在多个边框之中)。因此,使用属性指定的class往往无法对组件内部细节进行微调。这时,还是要借助渲染时由框架自动指定的CSS class。

幸亏,CSS允许我们使用class的嵌套定义,例如加入我们有如下元素:

<div class="outterclass">
<div class="innerclass"/>
</div>
<div class="innerclass"/>

我们可以通过“.outterclass .innerclass”来为被包含在outterclass中的innerclass指定样式(class之间用空格分隔)。利用这一特性,我们可以将需要独立定义样式的组件包含在一个class相同的容器中,来实现为同一组件定义不同风格。例如我们有以下<w:button>:

<layout:panelGrid columns="3">
<layout:cell>
<span class="boldbtn"><w:button value="abc1" /></span>
</layout:cell>
<layout:cell>
<span class="normalbtn"><w:button value="abc2"/></span>
</layout:cell>
<layout:cell>
<span class="boldbtn"><w:button value="abc3"/></span>
</layout:cell>
<layout:cell>
<span class="italicbtn"><w:button value="abc4"/></span>
</layout:cell>
<layout:cell>
<span class="boldbtn"><w:button value="abc5"/></span>
</layout:cell>
<layout:cell>
<span class="italicbtn"><w:button value="abc6"/></span>
</layout:cell>
</layout:panelGrid>

利用它们外部的<span>标签上的class,就可以为它们独立定义样式:

.boldbtn .x-btn-text {
font-weight : bold;
}

.italicbtn .x-btn-text {
font-style : italic;
}

渲染结果如下:

6. 小结

AOM为我们预定义了多套皮肤,并提供了大量组件属性来定义组件的显示效果。但对于复杂多变的应用场景,仅依靠AOM自身封装的皮肤与属性可能无法满足所有需求,这时我们可以退一步,通过底层的CSS样式表来对展示样式进行精确控制。

奇怪的文章排序

张贴人: ??web 2008-06-16 01:29

天天盼着看新文,谁知这篇08-06-13 发的文章竟然躲到第三页来了,看来“文章”不只是按时间排序的。 估计不少朋友都没看到呢。 特此顶一下,看看能不能排到第一页去。

你做得很好!

张贴人: 飞天不不 2008-06-16 09:42

果然如你所料,被你顶到第一页来了