Friday 31 August 2012

CSS3 ellipsis functionality with PrimeFaces (Client side and server side)

Introduction

If you have a certain user UI requirement, you have basically 2 options when using JSF.
Since the output of the JSF system is in most cases just plain HTML, you can mixing the required CSS and javaScript to have your UI feature. Or you can have some server component that does most of the work and generates the required HTML from their renderers.
An example makes this situation clear.  In a project I had the requirement that on a screen there were a lot of long descriptions of codes that needed to be displayed.
In fact there were so much of them that the screen was very crowded when the full description of all codes was shown. The solution was that only a part of it was shown on the screen and when the mouse hoovered over, the complete description was shown

Server side solution

As I'm much more experienced in writing Custom JSF components, I created a new PrimeFaces component that is capable of showing some truncated text. It is almost identical to the outputText component and has only one additional attribute called truncateAt.
You can define with it the maximum number of characters it shows before three dots are generated.  In the case the text has more characters then specified in the truncateAt attribute, the complete text is placed in the title property of the span element surrounding the shortened text.
This custom PrimeFaces component is quit easy and the important parts of the code responsible for the rendering is shown below:

        writer.startElement("span", null);
        writer.writeAttribute("id", clientId, null);
        String value = outputText.getValue().toString();
        Integer truncateAt = outputText.getTruncateAt();
        if (truncateAt != null && value.length() > truncateAt) {
            writer.writeAttribute("title", value, null);
            value = value.substring(0, truncateAt-1)+"...";
        }

        writer.writeText(value, null);
        writer.endElement("span");
You can improve the above code by searching the words in the description and not slash words in two.

After creating a simple POJO for the component itself (extending HtmlOutputText) and registering your work in the faces-config.xml
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
                version="2.0">
    <namespace>http://www.c4j.be/primefaces</namespace>

    <tag>
        <tag-name>outputText</tag-name>
        <component>
            <component-type>be.c4j.jsf.primefaces.outputtext.OutputText</component-type>
            <renderer-type>be.c4j.jsf.primefaces.outputtext.OutputTextRenderer</renderer-type>
        </component>
        <attribute>
        ....
    </tag>


You have your required functionality.

Cient side solution


Afterwards, some people pointed me to the CSS3 ellipsis (text-overflow attribute) as an alternative solution.  With some standard PrimeFaces components and the use of a proper css style definition, you can achieve the same effect.

The css style class looks like this
.ellipsis {
    display: inline-block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 400px;
    vertical-align: bottom;
}


where the width property can be varied to your convenience.  The central element here is the text-overflow: ellipsis line.  Since you can only specify the size of a html element in block mode, you have to specify the display: inline-block to allow more information on the same 'line'

Within the JSF view we can use the tooltip PrimeFaces component to show us the long description when we hoover over the truncated area.
<h:panelGroup styleClass="ellipsis" id="longText">${bean.text}</h:panelGroup>
<p:tooltip for="longText" value="#{bean.text}"></p:tooltip>

Differences of solutions


Both solutions are resulting in more or less the same visual and functional result in the browser but there are a few small differences.
  • With the client side solution, the truncation of the description is performed based on the size it takes on the screen.  When the user changes the font size, there are a different number of characters displayed. With the server side solution, the number of characters is always the same, despite of the user font settings.
  • The tooltip is only shown when some truncation of the description occurs in the server side solution in contract to always for the client side solution.  This can be changed by adapting the rendering code and always add the description to the title property.  However showing the same text in the tooltip isn't common practice.

Comparison of solution


As said, both solutions are resulting in more or less the same visual and functional result in the browser but each has his pro and contra. An overview of them

pro server side
  • Works on all browsers, no CSS3 support required
  • No CSS required, good for CSS/JavaScript newbies (I'm one of them)
  • Advanced algorithm determining the ... position easy to implement.

contra server side
  • Custom JSF component required (you need to use another tag in the JSF view if you want the functionality)
  • Less skinnable (look and feel changes by just changing CSS and JavaScript)

For the client side solution, the pro and contras are basically switched.

pro client side
  • No special/custom JSF component required, only the tooltip component of PrimeFaces
  • Skinnable
  • Screen layout is better preserved when user changes font size in browser.

contra client side
  • CSS3 browser support required.  This time Firefox was very late to include support for it and it is only available since version 7.
  • Positioning of the ... is difficult to align with the word boundaries.

Conclusion


I still prefer the server side solution, the one I used in the project.  I find it easy to create a simple JSF custom component where I'm in control of the result in Java and don't need any CSS3 or other client side solution to have the functionality that I need.
But for all the other people that like playing with those things, you can see how easy it is to do it with PrimeFaces.

You can find an example demo here.

No comments:

Post a Comment