联合使用 CSS、JavaScript 和 JSF 精心打造 Ajax 应用程序,第 1 部分: 增强 JSF 页面的外观

探索标准 JSF 组件中的 CSS 支持

典型的 Web 应用程序都需要使用级联样式表(Cascading Style Sheets,CSS)、JavaScript 和服务器端的框架(比如 JavaServer Faces,JSF)。CSS 让您可以在 Ajax 和其他应用程序内部更改 Web 组件的可视特征,以使它们具备与众不同的友好外观。在这个包含两部分的系列文章的第 1 部分中,您将了解如何使用标准 JSF 组件的一些与 CSS 相关的特性。此外,您还将了解如何创建一个可用来设置嵌套组件默认样式的定制 JSF 组件,以确保 Web 应用程序的所有页面具有一致的外观。您还可以使用这个技巧来程序化设置其他的组件属性,本系列的第 2 部分将展示如何借助 JavaScript 让 JSF 表单更加动态。

Andrei Cioroianu, 高级 Java 开发人员和顾问, Devsphere

Andrei Cioroianu 是 Devsphere 公司的一名高级 Java 开发人员和顾问,该公司专门提供定制 Java EE 开发服务以及 Ajax/JSF 顾问服务。您可以通过 www.devsphere.com 的联系表单与 Andrei 联系。



2008 年 3 月 07 日

使用 JSF 组件的样式属性

几乎每个 JSF HTML 组件都有两个可选属性,分别为 stylestyleClass,这两个属性在 HTML 输出中呈现为 styleclass 属性。一些组件,比如 <h:dataTable><h:panelGrid>,还有针对于其 facet 的额外样式属性。本节讨论与 CSS 相关的 JSF HTML 库属性。

将 CSS 文件链接到 JSF 页面

如果 Web 页面具有独特的样式,可以在页面页眉的 <style> 元素内定义样式规则。也可以使用 style 属性为单个 JSF 组件指定样式信息。但在大多数情况下,您可能更愿意在单独的 CSS 文件中包含样式规则,以便可以将这些样式规则应用到多个页面。可以使用 <link> 标记将外部样式表链接到一个 Web 页面,如清单 1 所示:

清单 1. 使用 <link> 标记
<link rel="stylesheet" type="text/css"
      href="${pageContext.request.contextPath}/MyStyles.css">
<link rel="stylesheet" type="text/css"
      href="<%=request.getContextPath()%>/styles/MoreStyles.css">

在清单 1 中,href 属性包含绝对 URI。也可以为 CSS 文件使用相对链接,但如果 JSF 页面包含 /faces/ 前缀,最好是在上下文路径中包含 ${pageContext.request.contextPath}<%=request.getContextPath()%>。在这种情况下,任何链到 CSS 文件、图像或其他资源的相对链接都将导致一个包含 /faces/ 前缀的 HTTP 请求,这意味着非 JSF 文件将由 Faces servlet 处理。这虽然可以工作,但效率并不高。若使用 .faces 后缀请求 JSF 页面,则尽可以放心地在基于 JSF 的应用程序中使用相对链接,因为这样可以消除上述问题。

定义和使用样式规则

CSS 文件包含一些样式表规则,这些规则可以应用于 JSF 组件能够呈现的 HTML 元素,也可以被简单地包含在 JSF 页面的组件之间。比如,如果希望只有将鼠标放到链接上时才出现下划线,则可以使用如下规则(参见清单 2):

清单 2. 链接的样式规则
a { text-decoration: none; }
a:hover { text-decoration: underline; }

这些规则可应用于任何链接,无论 <a> 元素是直接包含在 JSF 页面还是由诸如 <h:commandLink> 这样的 JSF 组件生成(参见清单 3):

清单 3. HTML 和 JSF 链接
<a href="LinkStyles.faces">HTML Link</a>
<h:commandLink value="JSF Link"/>

清单 4 给出了如何为 JSF 组件指定内联样式。

清单 4. 具有样式属性的 JSF 组件
<h:commandLink value="Red Link" style="color: red"/>

然而在大多数情况下,都是在 CSS 文件中定义样式类(参见清单 5),以便可以将相同的样式规则应用于多个组件。

清单 5. 样式类示例
.GreenClass { color: green; }

此样式类的名称将会通过 styleClass 属性提供给 JSF 组件,如清单 6 所示。

清单 6. 具有 styleClass 属性的 JSF 组件
<h:commandLink value="Green Link" styleClass="GreenClass"/>

清单 1-6 的代码可以从本文的示例代码链接给出的 LinkStyles.jspLinkStyles.css 文件中找到(参见 下载)。

具有多个样式类的 JSF 组件

正如之前所提到的,一些 JSF 组件具有多个样式属性。例如,<h:message><h:messages> 就有如下所示的 10 个与 CSS 相关的属性:

  • style
  • styleClass
  • errorClass
  • errorStyle
  • fatalClass
  • fatalStyle
  • infoClass
  • infoStyle
  • warnClass
  • warnStyle

根据所呈现的消息的严重程度,只有两个属性会应用到每个消息。UISelectOneUISelectMany 组件的 JSF 标记接受所呈现的列表项的 enabledClassdisabledClass 属性。<h:dataTable><h:panelGrid> 标记 则有针对主表、页眉、页脚、行和列的类属性。下面的示例展示了如何使用与 CSS 相关的数据表属性,这个数据表在下面的屏幕截图中给出。

图 1. TableStyles 示例
Tablestyles 示例

首先,需要为这个表组件建立数据模型。OrderBean 类(如清单 7 所示)扩展了 javax.faces.model.ArrayDataModel。实际的应用程序会从数据库中获得模型的数据,但为了简便起见,OrderBean 将由一个静态数组初始化。

清单 7. OrderBean 示例
package jsfcssjs;

import javax.faces.model.ArrayDataModel;

public class OrderBean extends ArrayDataModel {
    private static ItemBean items[] = new ItemBean[] {
        new ItemBean("Domain Name",        3,   7.99f),
        new ItemBean("SSL Certificate",    1, 119.00f),
        new ItemBean("Web Hosting",        1,  19.95f),
        new ItemBean("Email Box",         20,   0.15f),
        new ItemBean("E-Commerce Setup",   1,  25.00f),
        new ItemBean("Technical Support",  1,  50.00f)
    };

    public OrderBean(){
        super(items);
    }
    
    public float getTotalPrice(){
        float total = 0;
        for (int i = 0; i < items.length; i++)
            total += items[i].getItemPrice();
        return total;
    }
    
}

ItemBean 类(参见清单 8)具有三个读/写属性(descriptionquantityunitPrice)和一个只读属性(itemPrice)。

清单 8. ItemBean 示例
package jsfcssjs;

public class ItemBean implements java.io.Serializable {
    private String description;
    private int quantity;
    private float unitPrice;
    
    public ItemBean(){
    }
    
    public ItemBean(String description, int quantity, float unitPrice){
        this.description = description;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }
    
    ...

    public float getItemPrice(){
        return unitPrice * quantity;
    }
    
}

TableStyles.css 文件(如清单 9 所示)包含用于此数据表的样式。

清单 9. TableStyles.css 示例
body     { font-family: Arial; }
.tablebg { background-color: #D0D0A0; }
.header  { font-weight: bold; }
.footer  { font-weight: bold; }
.text    { text-align: left; }
.number  { text-align: right; }
.graybg  { background-color: #DDDDDD; }
.whitebg { background-color: #FFFFFF; }

TableStyles.jsp 示例(参见清单 10)包含一个 JSF 表组件,该组件使用在 TableStyles.css 中定义的样式类。styleClass 属性的值用于 <table> 元素。headerClassfooterClass 属性为此表的页眉和页脚单元格指定样式。columnClasses 属性包含用于数据单元格的 <td> 元素的类的列表(用逗号分隔)。

另外一个类的列表通过 rowClasses 属性指定。这些类用于此表的 <tr> 元素。在 TableStyles.jsp 示例中,只有两个行类,但此表却有 6 行。<h:dataTable> 组件的 JSF 呈现器会交替使用这两个样式类,为第一、三和五行使用 graybg 类,为第二、四和六行使用 whitebg 类。var 属性定义一个变量,称为 row,可用来在呈现期间访问当前行的数据。全部数据从 orderBean 模型获得,该模型通过 value 属性绑定到此表。

清单 10. TableStyles.jsp 示例
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<f:view>
<html>
<head>
    <title>Order Table</title>
    <link rel="stylesheet" type="text/css"
        href="<%=request.getContextPath()%>/styles/TableStyles.css">
</head>
<body>
    <h1>Order Table</h1>
    <h:dataTable id="table" var="row" value="#{orderBean}"
            styleClass="tablebg" headerClass="header" footerClass="footer"
            columnClasses="text,number,number,number"
            rowClasses="graybg,whitebg" border="0" cellpadding="5">
        <h:column>
            <f:facet name="header">
                <h:outputText value="Description"/>
            </f:facet>
            <h:outputText value="#{row.description}"/>
            <f:facet name="footer">
                <h:outputText value="Total Price"/>
            </f:facet>
        </h:column>
        <h:column>
            <f:facet name="header">
                <h:outputText value="Quantity"/>
            </f:facet>
            <h:outputText value="#{row.quantity}"/>
        </h:column>
        <h:column>
            <f:facet name="header">
                <h:outputText value="Unit Price"/>
            </f:facet>
            <h:outputText value="#{row.unitPrice}">
                <f:convertNumber type="currency" currencyCode="USD"
                    minFractionDigits="2" maxFractionDigits="2"/>
            </h:outputText>
        </h:column>
        <h:column footerClass="footer number">
            <f:facet name="header">
                <h:outputText value="Item Price"/>
            </f:facet>
            <h:outputText value="#{row.itemPrice}">
                <f:convertNumber type="currency" currencyCode="USD"
                    minFractionDigits="2" maxFractionDigits="2"/>
            </h:outputText>
            <f:facet name="footer">
                <h:outputText value="#{orderBean.totalPrice}">
                    <f:convertNumber type="currency" currencyCode="USD"
                        minFractionDigits="2" maxFractionDigits="2"/>
                </h:outputText>
            </f:facet>
        </h:column>
    </h:dataTable>
</body>
</html>
</f:view>

清单 11 给出了由 TableStyles.jsp 页面生成的 HTML。

清单 11. 由 TableStyles.jsp 生成的 HTML
<html>
<head>
    <title>Order Table</title>
    <link rel="stylesheet" type="text/css"
        href="/jsf12css/styles/TableStyles.css">
</head>
<body>
    <h1>Order Table</h1>

<table id="table" class="tablebg" border="0" cellpadding="5">
<thead>
<tr>
<th class="header" scope="col">Description</th>
<th class="header" scope="col">Quantity</th>
<th class="header" scope="col">Unit Price</th>
<th class="header" scope="col">Item Price</th>
</tr>
</thead>
<tfoot>
<tr>
<td class="footer">Total Price</td>
<td class="footer"></td>
<td class="footer"></td>
<td class="footer number">$240.92</td>
</tr>
</tfoot>
<tbody>
<tr class="graybg">
<td class="text">Domain Name</td>
<td class="number">3</td>
<td class="number">$7.99</td>
<td class="number">$23.97</td>
</tr>
<tr class="whitebg">
<td class="text">SSL Certificate</td>
<td class="number">1</td>
<td class="number">$119.00</td>
<td class="number">$119.00</td>
</tr>
<tr class="graybg">
...
</tr>
<tr class="whitebg">
...
</tr>
<tr class="graybg">
...
</tr>
<tr class="whitebg">
...
</tr>
</tbody>
</table>

</body>
</html>

TableStyles.jsp 示例中的 JSF 表的最后一个 <h:column> 组件使用 footerClass 属性为显示订单总额的页脚单元格指定两个类。footernumber 类将会用于页脚单元格的 <td> 元素。<h:column> 组件的 footerClass 属性只在 JSF 1.2 中可用。如果使用的是较早的 JSF 版本,则必须包装能在 <div> 元素内呈现总额的 <h:outputText> 组件,以便可以添加 number 类的对齐效果(参见清单 12)。源代码的压缩文件包含针对 JSF 1.1 和 JSF 1.2 的代码示例。

清单 12. JSF 1.1 版本的 TableStyles.jsp 示例
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<f:view>
    ...
    <h:dataTable ...>
        ...
        <h:column>
            ...
            <f:facet name="footer">
                <h:panelGroup>
                    <f:verbatim><div class="number"></f:verbatim>
                    <h:outputText value="#{orderBean.totalPrice}">
                        <f:convertNumber type="currency" currencyCode="USD"
                            minFractionDigits="2" maxFractionDigits="2"/>
                    </h:outputText>
                    <f:verbatim></div></f:verbatim>
                </h:panelGroup>
            </f:facet>
        </h:column>
    </h:dataTable>
    ...
</f:view>

为 JSF 组件设置默认样式

在构建具有很多 JSF 页面的大型应用程序时,您可能不喜欢手动设置每个组件实例的 styleClass 属性。标准的 JSF HTML 组件没有默认样式,但是您将在本节中看到,可以遍历此 JSF 组件树并程序化地设置默认的样式类。要实现此操作,需要知道这些树是如何创建的以及此 HTML 是如何生成的。

理解 JSF 组件树如何构建

所有的 face 请求均由 Faces Servlet 截获,这是在每个基于 JSF 的 Web 应用程序的 web.xml 描述符中配置的。对于每个请求,Faces Servlet 都会初始化 javax.faces.context.FacesContext 对象并调用 javax.faces.lifecycle.Lifecycle 实例的 execute()render() 方法。execute() 方法处理 JSF 请求处理生命周期中除最后一个阶段(Render Response)之外的每个阶段,最后的这个阶段由 render() 方法执行。

在第一个阶段(名为 Restore View),JSF 实现获取应用程序的视图处理程序,它是一个 javax.faces.application.ViewHandler 实例,如果请求是一个 post back(比如,用户单击了提交按钮),还会调用 restoreView() 方法。在这种情况下,此组件树应该在处理之前的 Faces 请求期间构造和保存(参见侧栏 状态保存方法)。

如果当前的请求不是一个 post back(比如,用户单击了一个 Faces 链接),JSF 实现调用当前 FacesContextrenderResponse() 方法和 ViewHandler 实例的 createView() 方法。renderResponse() 调用会指导 JSF 实现由当前的 Restore View 阶段跳到 Render Response 阶段。默认视图处理程序的 createView() 方法只创建一个 javax.faces.component.UIViewRoot 组件。此树的其余部分将会在 Render Response 阶段创建,在该阶段 JSF 实现调用视图处理程序的 renderView() 方法。

不管是恢复还是创建组件树,JSF 实现都会在 renderView() 方法执行期间的某个阶段将 HTTP 请求转发给相关的 JSF 页面。比如,如果请求 /MyApp/MyPage.faces/MyApp/faces/MyPage.jsp,那么所执行的 JSF 页面的 URI 就是 /MyApp/MyPage.jsp

任何 JSF 页面都包含组件标记,比如 <f:view><h:form>,这些标记都受 JSP 标记处理程序支持。这是一些 Java™ 类,它们实现在 javax.servlet.jsp.tagext 包中定义的接口。当执行此页时,JSP 容器会创建标记处理程序的实例并调用它们的方法。JSF 1.2 组件的所有标记处理程序都扩展了 javax.faces.webapp.UIComponentELTag。如果使用的是 JSF 1.1 API,则此标记处理程序类扩展 javax.faces.webapp.UIComponentTagjavax.faces.webapp.UIComponentBodyTag。不管使用的是哪个 JSF 版本,如果组件树没有在 Restore View 阶段恢复,JSF 标记处理程序将会在 Render Response 阶段创建组件实例。

您可能会尝试通过实现 javax.faces.event.PhaseListener 来设置默认的样式,但当用户以单击链接或在 Web 浏览器中输入 URL 的方式请求页面时,这种方法不会生效。在这种情况下,JSF 实现不能恢复组件树,此树必须在 Render Response 阶段创建。在最后阶段之前,组件树并不存在,在呈现了响应之后再更新组件样式就太晚了,因为 HTML 输出已经生成。因此,默认的样式必须在呈现组件之前的 Render Response 阶段执行期间设置。

在呈现之前遍历组件树

JSF 组件有一个有趣的只读属性,称为 rendersChildren。如果该属性为 false,只有 encodeBegin()encodeEnd() 方法会被调用来呈现此组件。如果 getRendersChildren() 返回 true,那么 encodeChildren() 也会在 Render Response 阶段被调用。这些方法通常会将呈现指派给另一个类,该类必须实现 javax.faces.render.Renderer 接口,但 JSF 组件也可以生成 HTML,无需使用呈现器。

当使用 JSF 1.1 时,编码方法最初都由标记处理程序方法调用。但是,如果组件呈现自身的子组件,那么嵌套组件的所有标记处理程序都会停止调用编码方法,因为呈现将会由另一些组件负责,这些组件的 getRendersChildren() 方法返回 true。这种在 JSF 1.1 中实现的呈现过程十分有效,因为它将缓冲降到了最低,但它需要应用程序开发人员在 <f:verbatim> 组件中包含 HTML 内容。在 JSF 1.2 中,由于呈现只会在整个组件树创建之后才执行,所以不需要使用 <f:verbatim>

不管使用的是 JSF 1.1 还是 1.2,只要调用了 encodeChildren(),嵌套组件都会被完全初始化并准备好被呈现。因此,此时就是设置默认样式的最佳时机。接下来,您会看到如何创建一个定制组件来为它的所有嵌套组件设置 styleClass 属性,递归遍历 JSF 组件树。

这里所展示的技巧可以用来设置 JSF 组件的任何属性,而不仅仅是默认的样式。因而,最好是创建一个可重用的通用基类。SetupComponent 类扩展 JSF API 的 UIComponentBase 类,并定义一个名为 setup() 的抽象方法,可以对支持呈现的每个嵌套组件调用该方法。setupTree() 方法(如清单 13 所示)执行 JSF 组件树的递归遍历。

清单 13. SetupComponent 类的 setup() 和 setupTree() 方法
package jsfcssjs;

import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
...
public abstract class SetupComponent extends UIComponentBase {

    protected abstract void setup(FacesContext ctx, UIComponent comp);

    protected void setupTree(FacesContext ctx, UIComponent comp){
        if (!comp.isRendered())
            return;
        setup(ctx, comp);
        for (UIComponent child : comp.getChildren())
            setupTree(ctx, child);
    }
    ...
}

继承自 UIComponentBasegetRendersChildren() 方法(参见清单 14)被覆盖以返回 true,以便设置组件能够控制其子组件的呈现。

清单 14. SetupComponent 类的 getRendersChildren()方法
public abstract class SetupComponent extends UIComponentBase {
    ...
    public boolean getRendersChildren(){
        return true;
    }
    ...
}

清单 15 中所示的 encodeChildren() 方法被覆盖,以便在为每个子组件调用 encodeTree() 之前调用 setupTree()

清单 15. SetupComponent 类的 encodeChildren() 方法
public abstract class SetupComponent extends UIComponentBase {
    ...
    public void encodeChildren(FacesContext ctx)
            throws IOException {
        if (!isRendered())
            return;
        setupTree(ctx, this);
        for (UIComponent child : getChildren())
            encodeTree(ctx, child);
    }
    ...
}

encodeTree() 方法(参见清单 16)为当前组件调用编码方法,如果 getRendersChildren()返回 false,则还会为子组件递归调用此方法。

清单 16. SetupComponent 类的 encodeTree() 方法
public abstract class SetupComponent extends UIComponentBase {
    ...
    protected void encodeTree(FacesContext ctx, UIComponent comp)
            throws IOException {
        if (!comp.isRendered())
            return;
        comp.encodeBegin(ctx);
        if (comp.getRendersChildren())
            comp.encodeChildren(ctx);
        else
            for (UIComponent child : comp.getChildren())
                encodeTree(ctx, child);
        comp.encodeEnd(ctx);
    }

}

创建和配置一个定制组件

清单 17 中所示的 DefaultStylesComponent 类扩展 SetupComponent 并实现 setup() 方法,以设置 styleClass 属性,此属性的值构建在组件系列的名称和呈现器的名称之上。

清单 17. DefaultStylesComponent 类
package jsfcssjs;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import java.util.Map;

public class DefaultStylesComponent extends SetupComponent {

    protected void setup(FacesContext ctx, UIComponent comp){
        Map<String, Object> attrMap = comp.getAttributes();
        if (attrMap.get("styleClass")!= null)
            return;
        String familyName = getLastName(comp.getFamily());
        String rendererName = getLastName(comp.getRendererType());
        if (familyName == null || rendererName == null)
            return;
        String className = "Default" + familyName;
        if (!familyName.equals(rendererName))
            className += rendererName;
        attrMap.put("styleClass", className);
    }
    
    protected String getLastName(String fullName){
        if (fullName == null)
            return null;
        int dotIndex = fullName.lastIndexOf('.');
        if (dotIndex != -1)
            return fullName.substring(dotIndex+1);
        else
            return fullName;
    }

    public String getFamily(){
        return "DefaultStyles";
    }

}

DefaultStylesComponentfaces-config.xml 中配置,如清单 18 所示。

清单 18. 在 faces-config.xml 中配置 DefaultStylesComponent
<faces-config ...>
    ...
    <component>
        <component-type>DefaultStylescomponent</component-type>
        <component-class>jsfcssjs.DefaultStylesComponent</component-class>
        <component-extension>
            <component-family>DefaultStyles</component-family>
        </component-extension>
    </component>

</faces-config>

实现定制标记

DefaultStylesTag 类(参见清单 19)扩展由 JSF 1.2 API 定义的 UIComponentELTag 类,并实现 getComponentType()getRendererType() 方法以分别返回 DefaultStylesComponentnull。如果使用的是 JSF 1.1,标记类必须扩展 UIComponentTag,而非 UIComponentELTag

清单 19. DefaultStylesTag 类
package jsfcssjs;

import javax.faces.webapp.UIComponentELTag;

public class DefaultStylesTag extends UIComponentELTag {

    public String getComponentType(){
        return "DefaultStylesComponent";
    }

    public String getRendererType(){
        return null;
    }

}

css.tld 文件(如清单 20 所示)定义了定制标记的 defaultStyles 名称和属性。对 idbindingrendered 属性的支持均继承自 UIComponentELTag

清单 20. 用于 JSF 1.2 和 JSP 2.1 的 css.tld 文件
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee" ... version="2.1">
    <tlib-version>1.0</tlib-version>
    <short-name>css</short-name>
    <uri>/css.tld</uri>
    <tag>
        <name>defaultStyles</name>
        <tag-class>jsfcssjs.DefaultStylesTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>id</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>binding</name>
            <required>false</required>
            <deferred-value>
                <type>jsfcssjs.DefaultStylesComponent</type>
            </deferred-value>
        </attribute>
        <attribute>
            <name>rendered</name>
            <required>false</required>
            <deferred-value>
                <type>boolean</type>
            </deferred-value>
        </attribute>
    </tag>
</taglib>

如果使用的是 JSF 1.1,TLD 文件应该遵从 JSP 1.2 规范定义的语法(参见清单 21)。

清单 21. 用于 JSF 1.1 和 JSP 1.2 的 css.tld 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
    "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>css</short-name>
    <uri>/css.tld</uri>
    <tag>
        <name>defaultStyles</name>
        <tag-class>jsfcssjs.DefaultStylesTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>id</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>binding</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>rendered</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
</taglib>

在 JSF 页使用定制组件

本节中给出的 JSF 示例展示了如何使用背景颜色和边框来增强 JSF 组件的外观。

图 2. DefaultStyles 示例
defaultstyles 示例

清单 22 展示的是 DefaultStyles.css 文件,该文件定义了几个用于 DefaultStyles.jsp 示例的样式类。

清单 22. DefaultStyles.css 示例
.DefaultForm {}
.DefaultPanelGrid { background-color: #FFFFFF; }
.DefaultInputText { background-color: #FFDDDD;
    border-style: ridge; border-width: thin; }
.DefaultInputTextarea { background-color: #DDFFDD;
    border-style: groove; border-width: thick; }
.DefaultSelectOneMenu { background-color: #DDDDFF;
    border-style: inset; border-width: medium; }
.DefaultCommandButton { background-color: #DDDDDD;
    border-style: outset; border-width: medium; }
.SpecialInputText { background-color: #DDFFFF;
    border-style: double; border-width: thick; }
.EnabledOption { color: #FF0000; }
.DisabledOption { color: #0000FF; }

DefaultStyles.jsp 示例 (参见清单 23)使用了 <css:defaultStyles> 组件为不具有 styleClass 属性的 JSF 组件设置默认样式。其中的一个组件使用 SpecialInputText 样式类。EnabledOptionDisabledOption 类用于下拉列表项。

清单 23. DefaultStyles.jsp 示例
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="css" uri="/css.tld" %>

<f:view>
<html>
<head>
    <title>Default Styles</title>
    <link rel="stylesheet" type="text/css"
        href="<%=request.getContextPath()%>/styles/DefaultStyles.css">
</head>
<body>
    <h1>Default Styles</h1>
    <css:defaultStyles>
        <h:form id="form">
            <h:panelGrid columns="1" border="0" cellspacing="5">
                <h:inputText id="text" size="30" value="default text style"/>
                <h:inputText id="special" size="30" value="special text style"
                    styleClass="SpecialInputText"/>
                <h:selectOneMenu id="menu" enabledClass="EnabledOption"
                        disabledClass="DisabledOption">
                    <f:selectItem itemValue="First" itemLabel="First"/>
                    <f:selectItem itemValue="Second" itemLabel="Second"/>
                    <f:selectItem itemValue="Third" itemLabel="Third"
                        itemDisabled="true"/>
                </h:selectOneMenu>
                <h:inputTextarea id="area" rows="5" cols="30"
                    value="default text area style"/>
                <h:commandButton id="button" value="Submit"/>
            </h:panelGrid>
        </h:form>
    </css:defaultStyles>
</body>
</html>
</f:view>

清单 24 给出了由 DefaultStyles.jsp 页面产生的 HTML。您将注意到所有的表单元素都有一个 class 属性。以 Default 开始的那些类名均由 <css:defaultStyles> 组件设置。

清单 24. DefaultStyles.jsp 产生的 HTML
<html>
<head>
    <title>Default Styles</title>
    <link rel="stylesheet" type="text/css"
        href="/jsf12css/styles/DefaultStyles.css">
</head>
<body>
    <h1>Default Styles</h1>
    
<form id="form" ... class="DefaultForm" ...>
<input type="hidden" name="form" value="form" />
<table class="DefaultPanelGrid" border="0" cellspacing="5">
<tbody>
<tr>
<td><input id="form:text" type="text" ... 
    class="DefaultInputText" .../></td>
</tr>
<tr>
<td><input id="form:special" type="text" ... 
    class="SpecialInputText" .../></td>
</tr>
<tr>
<td><select id="form:menu" ... class="DefaultSelectOneMenu" ...>
    <option value="First" class="EnabledOption">First</option>
    <option value="Second" class="EnabledOption">Second</option>
    <option value="Third" disabled="disabled" 
        class="DisabledOption">Third</option>
</select></td>
</tr>
<tr>
<td><textarea id="form:area" ... 
    class="DefaultInputTextarea" ...> ... </textarea></td>
</tr>
<tr>
<td><input id="form:button" type="submit" ... 
    class="DefaultCommandButton" /></td>
</tr>
</tbody>
</table>
<input type="hidden" ... id="javax.faces.ViewState" value="..." />
</form>

</body>
</html>

结束语

本文讨论了标准 JSF 组件提供的 CSS 支持。简单的组件只有两个可选属性,分别为 stylestyleClass。更复杂的组件,比如网格和数据表,则会有针对其 facet 和元素的额外的 CSS 属性。本系列的这一部分还介绍了 JSF 如何构建组件树,以及如何在组件呈现之前遍历这些组件树以便可以添加特性,比如默认样式。请继续留意本系列的第 2 部分,在该部分您将会获得可以让 JSF 表单更加动态的基于 JavaScript 的技巧。


下载

描述名字大小
本文的示例应用程序jsfcssjs_part1_src.zip25KB

参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • developerWorks Web 开发专区 包含 Web 2.0 开发所需的工具和信息。
  • 获得更多有关 JSF 技术 的信息并将其应用到您的应用程序中。
  • developerWorks Ajax 资源中心 包含了不断增加的 Ajax 内容库和有用的资源,可以让您立即开始开发 Ajax 应用程序。
  • JSF 1.2 Specification 全面描述了 JavaServer Faces 技术。
  • W3C 提供了一个关于 级联样式表 (CSS) 的出色介绍。

获得产品和技术

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development, Java technology
ArticleID=293702
ArticleTitle=联合使用 CSS、JavaScript 和 JSF 精心打造 Ajax 应用程序,第 1 部分: 增强 JSF 页面的外观
publish-date=03072008