Showing posts with label CSS. Show all posts
Showing posts with label CSS. Show all posts

Tuesday, December 18, 2012

Elegant truncated text with ellipses in web

Sometimes text is big than allowed space. Assume you have a table column with a very long text and don't want to have line breaks or make the table bigger in order to show the text completely. Having a fluid truncated (clipped) text with three dots at the end would be a good option. It would be nice to show a truncated text with ... and put a tooltip which displays the whole text on mouseover. Furthemore, the text should display more content or less content dynamically when resizing browser and the available space gets bigger or shrunk. How to deal with fluid truncated text? Well, there are some JavaScript solutions with advanced configuration. One of them is e.g. dotdotdot and the another one is trunk8

But in simple cases we don't need JavaScript at all. There is a CSS property text-overflow: ellipsis which works fine on container elements (like div) with white-space: nowrap and overflow: hidden. I defined a style class truncate which can be applied to any container element.
.truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
    -ms-text-overflow: ellipsis;
    display: block;
    position: absolute;
}
This works smoothly. Example:
<div class="truncate">
    My very very very very very very very very very long text
</div>
The text will fit the space of the div element now.

As I develop JSF applications I need it for text in PrimeFaces p:column (rendered as td element). For that we need to set a max. width on p:column. For my project I decided to restrict the max. width to 160px. Style classes applied to p:column and the inner div (containing the text) look as follows:
.truncate {
    max-width: 160px;
    width: 160 px\9;
}

.truncate > div {
    width: 160 px\9;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
    -ms-text-overflow: ellipsis;
    display: block;
    position: absolute;
}
\9 is a hack for IE7 / IE8. Unfortunately, I could not do text in table's columns fluid for IE7 / E8, so that I set a fix width 160px. The code in XHTML is like this one:
<p:column headerText="Description" sortBy="#{user.description}" styleClass="truncate">
    <h:outputText id="desc" value="#{user.description}"/>
    <pe:tooltip for="desc" value="#{user.description}"/>
</p:column>
And a final picture to complete this blog post.


The visible text's content (in the column "Description") is adjusted automatically while the table changes its size. There are no line breaks in columns when some text doesn't fit the available column's space. The table looks compacted.

Sunday, August 5, 2012

Bundled PrimeFaces Themes

I have created a sub-project in PrimeFaces Extensions for everybody who would not like to confront with adding multiply theme JAR files and only needs one bundled JAR with all PrimeFaces themes. One JAR file would also reduce the scanning time during startup (JSF, CDI, ... look for marker XML files) in comparison to 35+ separate files.

Last release of the bundled themes can be found in the Maven Central repository. The release version is synchronized with the current release of PrimeFaces themes. Add this dependency to your pom.xml and you are done.
<dependencies>
    ...    
    <dependency>
        <groupId>org.primefaces.extensions</groupId>
        <artifactId>all-themes</artifactId>
        <version>1.0.6</version>
    </dependency>
    ...
</dependencies>
Non Maven users can download the JAR file direct from the Maven Central repository.

You can consider this as an "all-in-one" themes add-on. We don't modify themes, we only collect them by means of the Maven Assembly Plugin. A simple Maven project aggregates PrimeFaces themes and builds a single JAR file with all available themes. Here is a hint how it works. The Maven project with packaging pom is configured as follows:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
            <executions>
                <execution>
                    <id>package-all</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <descriptors>
                            <descriptor>src/main/assembly/all-themes.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.primefaces.themes</groupId>
        <artifactId>afterdark</artifactId>
        <version>${primefaces.theme.version}</version>
        <scope>runtime</scope>        
    </dependency>
    ...
    <dependency>
        <groupId>org.primefaces.themes</groupId>
        <artifactId>vader</artifactId>
        <version>${primefaces.theme.version}</version>
        <scope>runtime</scope>        
    </dependency>
</dependencies>

<properties>
    <primefaces.theme.version>1.0.6</primefaces.theme.version>    
</properties>
The Maven Assembly Plugin takes all dependencies and re-packs them as a single JAR. Repacking instructions are defined in the assembly descriptor all-themes.xml.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="...">
    <id>all-themes</id>
    <formats>
        <format>jar</format>
    </formats>

    <includeBaseDirectory>false</includeBaseDirectory>

    <dependencySets>
        <dependencySet>
            <unpack>true</unpack>
            <useProjectArtifact>false</useProjectArtifact>
            <useTransitiveDependencies>false</useTransitiveDependencies>
        </dependencySet>
    </dependencySets>
</assembly>
That's all.

Sunday, April 15, 2012

High Performance Webapps. Use Data URIs. Practice.

This post continues the started discussion about data URIs. The first question is if it's worth to put data URIs in style sheets? Yes, it's worth. First, I would like to point you to this great article "Data URIs for CSS Images: More Tests, More Questions" where you can try to test all three scenarios for your location. Latency is different depending on your location. But you can see a tendency that a web page containing data URIs is loaded faster. We can see one of the main tricks to achieve better performance with data URIs:

Split your CSS in two files - one with main data and one with data URIs only and place the second one in the footer. "In the footer" means close to the HTML body tag. Page rendering feels faster then because of the progressive rendering. In the second article you can see that this technique really accelerates page rendering. Style sheet in footer leads to a nice effect that large images download in parallel with the data URI style sheet. Why? Well, browser thinks stuff placed in footer can not have any impact on page structure above included files and doesn't block resource loading. I also read that in this case all browsers (except old IE versions) render a page immediately without waiting until CSS with data URIs has been loaded. The same is valid for JavaScript files, as far as I know. Is it valid at all to put CSS files in page footer? Well, it's not recommended in the HTML specification. But it's valid in practice and it's not bad at all in special cases. There is an interesting discussion on Stackoverflow "How bad is it to put a CSS include in the middle of the body?"

The second tip is to use data URIs for small images, up to 1-2 KB. It's not worth to use data URIs for large images. A large image has a very long data URI string (base64 encoded string) which can increase the size of CSS file. Files with a big size can block loading of other files. Remember, browsers have connection limitations. They can normally open 2-8 conection to the same domain. That means only 2-8 files can be loaded parallel at the same time. After reading some comments in internet I got an acknowledge about my assumption with 1-2 KB images.

We can soften this behavior by using of GZIP filter. A GZIP filter reduces size of resources. I have read that sometimes the size of an image encoded as data URI is even smaller than the size of original image. A GZIP filter is appled to web resources like CSS, JavaScript and (X)HTML files. But it's not recommended to apply it to images and PDF files e.g. So, not encoded images aren't going through the filer, but CSS files are going through. In 99%, if you gzip your CSS file, the resulting size is about the same as the regular image URL reference! And that was the third tip - use a GZIP filter.

I would like to show now my test results. My test environment: Firefox 11 on Kubuntu Oneiric. I prepared the showcase of PrimeFaces Extensions with 31 images which I added to the start page. These images display small themes icons in PNG format. Every image has the same size 30 x 27 px. Sizes in kilobytes lie in range 1.0 - 4.6 KB. CSS file without data URIs was 4.8 KB and with data URIs 91,6 KB. CSS files were included quite normally in HTML head section, by the way. I deployed showcases with and without data URIs on my VPS with Jetty 8 server. First without a GZIP filer. I cleared browser cache and opened Firebug for each showcase. Here results:

Without data URIs:

65 requests. Page loading time 3.84s (onload: 4.14s).

That means, document ready event occured after 3.84 sek. and window onload after 4.14 sek. Subsequent calls for the same page (resources were fetched from browser cache) took 577 ms, 571 ms, 523 ms, ...

With data URIs:

34 requests. Page loading time 3.15s (onload: 3.33s).

That means, fewer requests (remember 31 embedded images), document ready event occured after 3.15 sek. and window onload after 3.33 sek. Subsequent calls for the same page (resources were fetched from browser cache) took 513 ms, 529 ms, 499 ms, ...

There isn't much difference for subsequent calls (page refreshes), but there is a significant difference for the first time visiting. Especially onload event occurs faster with data URIs. No wonder. Images being loading after document is ready. Because they can not be loaded parallel (number of opened connection is limited), they get blocked. I took some pictures from Google Chrome Web Inspector. Below you can see timing for an image (vader.png) for the first (regular) case without data URI.


And the second case for the same image encoded as data URI.


You see in the second picture there isn't any blocking at all. Tests with a GZIP Filter didn't have much impact in my case (don't know why, maybe I haven't too much resources). Average times after a couple of tests with empty cache:

Without data URIs:

65 requests. Page loading time 3.18s (onload: 3.81s).

With data URIs:

34 requests. Page loading time 3.03s (onload: 3.19s).

Any questions?

Saturday, April 14, 2012

High Performance Webapps. Use Data URIs. Theory.

I continue to write tips for perfomance optimization of websites. The last post was about jQuery objects. This post is about data URIs. Data URIs are an interesting concept on the Web. Read "Data URIs explained" please if you don't know what it does mean. Data URIs are a technique for embedding resources as base 64 encoded data, avoiding the need for extra HTTP requests. It gives you the ability to embed files, especially images, inside of other files, especially CSS. Not only images are supported by data URIs, but embedded inline images are the most interesting part of this technique. This technique allows separate images to be fetched in a single HTTP request rather than multiple HTTP requests, what can be more efficient. Decreasing the number of requests results in better page performance. "Minimize HTTP requests" is actually the first rule of the "Yahoo! Exceptional Performance Best Practices", and it specifically mentions data URIs.

"Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages... 40-60% of daily visitors to your site come in with an empty cache. Making your page fast for these first time visitors is key to a better user experience."

Data URI format is specified as

data:[<mime type>][;charset=<charset>][;base64],<encoded data>

We are only interesting for images, so that mime types can be e.g. image/gif, image/jpeg or image/png. Charset should be omitted for images. The encoding is indicated by ;base64. One example of a valid data URI:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
        AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
        9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot">
HTML fragments with inline images like the above example are not really interesting because they are not cached. Data URIs in CSS files (style sheets) are cached along with CSS files and that brings benefits. Some advantages describing in Wikipedia:
  1. HTTP request and header traffic is not required for embedded data, so data URIs consume less bandwidth whenever the overhead of encoding the inline content as a data URI is smaller than the HTTP overhead. For example, the required base64 encoding for an image 600 bytes long would be 800 bytes, so if an HTTP request required more than 200 bytes of overhead, the data URI would be more efficient.
  2. For transferring many small files (less than a few kilobytes each), this can be faster. TCP transfers tend to start slowly. If each file requires a new TCP connection, the transfer speed is limited by the round-trip time rather than the available bandwidth. Using HTTP keep-alive improves the situation, but may not entirely alleviate the bottleneck.
  3. When browsing a secure HTTPS web site, web browsers commonly require that all elements of a web page be downloaded over secure connections, or the user will be notified of reduced security due to a mixture of secure and insecure elements. On badly configured servers, HTTPS requests have significant overhead over common HTTP requests, so embedding data in data URIs may improve speed in this case.
  4. Web browsers are usually configured to make only a certain number (often two) of concurrent HTTP connections to a domain, so inline data frees up a download connection for other content.
Furthermore, data URIs are better than sprites. Images organized as CSS sprites (many small images combined to one big) are difficult to be maintained. Maintenance costs are high. Imagine, you want to change some small images in the sprite, their position, size, color or whatever. Well, there are tools allowing to generate sprites, but later changes are not easy. Especially changes in size cause a shift of all positions and a lot of CSS changes. And don't forget - a sprite still requires one HTTP request :-).

What browsers support data URIs? Data URIs are supported for all modern browsers: Gecko-based (Firefox, SeaMonkey, Camino, etc.), WebKit-based (Safari, Google Chrome), Opera, Konqueror, Internet Explorer 8 and higher. For Internet Explorer 8 data URIs must be smaller than 32 KB. Internet Explorer 9 does not have this 32 KB limitation. IE versions 5-7 lack support of data URIs, but there is MHTML – when you need data URIs in IE7 and under.

Are there tools helping with automatic data URI embedding? Yes, there are some tools. The most popular is a command line tool CSSEmbed. Especially if you need to support old IE versions, you can use this command line tool which can deal with MHTML. Maven plugin for web resource optimization, which is a part of PrimeFaces Extensions project, has now a support for data URIs too. The plugin allows to embed data URIs for referenced images in style sheets at build time. This Maven plugin doesn't support MHTML. It's problematic because you need to include CSS files with conditional comments separately - for IE7 and under and all other browsers. How does the conversion to data URIs work?
  1. Plugin reads the content of CSS files. A special java.io.Reader implementation looks for tokens #{resource[...]} in CSS files. This is a syntax for image references in JSF 2. Token should start with #{resource[ and ends with ]}. The content inside contains image path in JSF syntax. Theoretically we can also support other tokens (they are configurable), but we're not interested in such kind of support :-) Examples:
    .ui-icon-logosmall {
        background-image: url("#{resource['images/logosmall.gif']}") !important;
    }
    
    .ui-icon-aristo {
         background-image: url("#{resource['images:themeswitcher/aristo.png']}") !important;
    }
    
  2. In the next step the image resource for each background image is localized. Images directories are specified according to the JSF 2 specification and suit WAR as well as JAR projects. These are ${project.basedir}/src/main/webapp/resources and ${project.basedir}/src/main/resources/META-INF/resources. Every image is tried to be found in those directories.
  3. If the image is not found in the specified directories, then it doesn't get transformed. Otherwise, the image is encoded into base64 string. The encoding is performed only if the data URI string is less than 32KB in order to support IE8 browser. Images larger than that amount are not transformed. Data URIs looks like
    .ui-icon-logosmall {
        background-image: url("data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgA ... ASUVORK5CYII=") !important;
    }
    
    .ui-icon-aristo {
        background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA ... BJRU5ErkJggg==") !important;
    }
    
Configuration in pom.xml is simple. To enable this feature set useDataUri flag to true. Example:
<plugin>
    <groupId>org.primefaces.extensions</groupId>
    <artifactId>resources-optimizer-maven-plugin</artifactId>
    <configuration>
        <useDataUri>true</useDataUri>
        <resourcesSets>
            <resourcesSet>
                <inputDir>${project.build.directory}/webapp-resources</inputDir>
            </resourcesSet>
        </resourcesSets>
    </configuration>
</plugin>
Enough theory in this post. The next one will describe a practice part. I will expose some measurements, screenshots and give tips how large images should be, where CSS should be placed, what is the size of CSS file with data URIs and whether a GZIP filter can help here. Stay tuned.

Saturday, October 29, 2011

Fix for CSS opacity in Internet Explorer

How to set CSS opacity in Internet Explorer? It's easy for good browsers like Firefox, Google Chrome or new IE9:
opacity: 0.5;
For old Safary versions you probably also need
-khtml-opacity: 0.5;
For IE6, IE7 you can write
filter: alpha(opacity=50);
and for IE8
-ms-filter: "alpha(opacity=50)";
Saidly, but filter with opacity value in IE isn't enough. Element should be positioned, in order that opacity would work in IE. If the element doesn't have a position, there is a little trick in order to get it working. Add 'zoom: 1' to your CSS.
filter: alpha(opacity=50);        // IE6, IE7
-ms-filter: "alpha(opacity=50)"; // IE8
zoom: 1;
I have faced this problem with Schedule component in PrimeFaces. The button "today" is not disabled in IE7 for the current day although it has the jQuery UI style .ui-state-disabled. The fix would be
/* IE7 hack */
div.fc-button-today.ui-state-disabled {
    *zoom: 1;
}
* means the style is applied for IE7 only.

Thursday, August 25, 2011

Combine jQuery Datepicker and Spinner for fast day incrementation / decrementation

Is it possible to add + / - buttons to jQuery UI Datepicker in order to increment / decrement days in a comfortable way? Yes, it's possible. I have done this task - see screenshot.


This is an extended PrimeFaces Calendar with added "dateSpinner" mode. I would like to omit JSF part and only show HTML / CSS / JavaScript part of such combined calendar. All what you need is to wrap the datepicker markup with a span element, add themable up / down arrows and use datepicker utility functions $.datepicker.formatDate(format, date, settings) and $.datepicker.parseDate(format, value, settings).

HTML
Original HTML with a calendar icon (called icon trigger in jQuery Datepicker) looks very simple
 
<input id="datepicker" class="hasDatepicker" type="text">
<img class="ui-datepicker-trigger" src="images/calendar.gif" alt="..." title="...">
 
You have to extend this as follows
<span id="datepickerWrapper" class="ui-ccalendar ui-widget ui-corner-all">
	<input id="datepicker" class="hasDatepicker" type="text">
	<img class="ui-datepicker-trigger" src="images/calendar.gif" alt="..." title="...">
	<a class="ui-ccalendar-button ui-ccalendar-up ui-corner-tr ui-button ui-widget ui-state-default ui-button-text-only">
		<span class="ui-button-text">
			<span class="ui-icon ui-icon-triangle-1-n"></span>
		</span>
	</a>
	<a class="ui-ccalendar-button ui-ccalendar-down ui-corner-br ui-button ui-widget ui-state-default ui-button-text-only">
		<span class="ui-button-text">
			<span class="ui-icon ui-icon-triangle-1-s"></span>
		</span>
	</a>
</span>

CSS
The most CSS classes above are jQuery UI classes. I marked own classes with ui-ccalendar. In the CSS part is very important to shift spinner's arrow buttons to left. I have shifted absolute positioned buttons with CSS statement right: 17px;. We achieve with this displacement that the calendar icon is visible.
.ui-ccalendar {
    display: inline-block;
    overflow: visible;
    padding: 0;
    position: relative;
    vertical-align: middle;
}

.ui-ccalendar-button {
    cursor: default;
    display: block;
    font-size: 0.5em;
    height: 50%;
    margin: 0;
    overflow: hidden;
    padding: 0;
    position: absolute;
    right: 17px;
    text-align: center;
    vertical-align: middle;
    width: 16px;
    z-index: 100;
}

.ui-ccalendar .ui-icon {
    left: 0;
    margin-top: -8px;
    position: absolute;
    top: 50%;
}

.ui-ccalendar-up {
    top: 0;
}

.ui-ccalendar-down {
    bottom: 0;
}

.ui-ccalendar .ui-icon-triangle-1-s {
    background-position: -65px -16px;
}

.ui-ccalendar .ui-icon-triangle-1-n {
    margin-top: -9px;
}

JavaScript
In this part I use the mentioned above utility functions formatDate() / parseDate() and setDate() / getDate() API of Date object to increment / decrement a single day. Month and year boundaries are considered automatically and in- / decremented if necessary. In- / decrementation logic is bound to mousedown event on spinner buttons.
var datepickerInput = $('#datepicker');
datepickerInput.datepicker({dateFormat: 'yy-mm-dd', ... other configuration if needed ...});

$('#datepickerWrapper').children('.ui-ccalendar-button').mouseover(function() {
	$(this).addClass('ui-state-hover');
}).mouseout(function() {
	$(this).removeClass('ui-state-hover');
}).mouseup(function() {
	$(this).removeClass('ui-state-active');
}).mousedown(function() {
	var el = $(this);
	el.addClass('ui-state-active');
	try {
		// get configured date format
		var dateFormat = datepickerInput.datepicker("option", "dateFormat");

		// extract a date from a string value with a specified format
		var date = $.datepicker.parseDate(dateFormat, datepickerInput.val());
		if (el.hasClass('ui-ccalendar-up')) {
			// increment day
			date.setDate(date.getDate() + 1);
		} else {
			// decrement day
			date.setDate(date.getDate() - 1);
		}

		// format a date into a string value with a specified format
		var strDate = $.datepicker.formatDate(dateFormat, date);
		datepickerInput.val(strDate);
	} catch (err) {
		// ignore and nothing to do
	}
});
Important is here "dateFormat" option. Date format normally depends on user locale and should be passed from outside.

P.S. Just now I found a desktop example of such combined calendar too. So, you see, it's an useful and established widget :-).

Wednesday, July 6, 2011

Cross-browser fonts (aka @font-face)

Recently I have experimented with web fonts to figure out the best one and take it as standard for my company. There are not much predefined web fonts at all. "Verdana" was too wide for me, "Arial" was too narrow, "Tahoma" and "Trebuchet MS" were different displayed dependent on browser, ... I ended up with a cross browser font face implementation. There are many wonderful fonts which can be added to web applications by using of @font-face. @font-face is a css rule which allows you to download a particular font from your server to render a webpage if the user hasn't got that font installed. @font-face is supported by all browsers, even old Internet Explorer. There are dozens of Internet ressources with quality and amazing fonts. For instance, go to the Font Squirrel. That's the best resource for free, high-quality, commercial-use fonts. Choose a font and download a @font-face kit for it (a zip file). You need four types of font files. Each @font-face kit come with:
  1. EOT fonts for Internet Explorer 4+
  2. TrueType fonts for Firefox 3.5+ , Opera 10+, Safari 3.1+, Chrome 4.0.249.4+
  3. WOFF fonts for Firefox 3.6+, Internet Explorer 9+, Chrome 5+
  4. SVG fonts for iPad and iPhone
After downloading unpack the zip file somewhere in your web application. You will find files with extensions .eot, .ttf, .woff, .svg and a stylesheet.css. stylesheet.css has ready-to-use definitions - examples how to add @font-face for the desired font to your web application. Assume, you have choosen a font named as "BPreplayRegular". @font-face will look like then as follows
@font-face {
    font-family: 'BPreplayRegular';
    src: url('BPreplay-webfont.eot');
    src: url('BPreplay-webfont.eot?#iefix') format('embedded-opentype'),
         url('BPreplay-webfont.woff') format('woff'),
         url('BPreplay-webfont.ttf') format('truetype'),
         url('BPreplay-webfont.svg#BPreplayRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}
To match your directory structure you should set right paths to your four files in "url", of course. After that you are able to apply the font to any HTML element, e.g.
body {
    font-family: 'BPreplayRegular', Verdana, sans-serif;
    padding: 0px;
    margin:  0px;
}
Such web fonts work fine and look identical in IE6-IE9, Firefox, Chrome, Safari, Opera. The good news - there are hundreds of free fonts which leave nothing to be desired.

Thursday, February 17, 2011

Two handy CSS hacks for Internet Explorer

Web developers often face with deficiencies of Internet Explorer. There are many CSS hacks for IE allowing to overcome deficiencies in most cases, but I always use two hacks which are valid for all IE versions. That are * html and \9. Examples:
 
* html .ui-spinner {
    vertical-align: middle;
}

.ui-spinner {
    vertical-align: middle\9;
}
 
Only IE understand them. Other browsers ignore these syntax.

Friday, June 25, 2010

How to make a fixed padding in HTML buttons?

If you have an input button in your HTML you maybe want to have a fixed padding around its label (text inside button). The padding is normally dependent on the label length and can look inordinate. These two buttons have for instance different spaces between labels and button borders.
<input type="button" value="OK">
<input type="button" value="Cancel">
The same padding is a simple task for most used browsers except Internet Explorer. Use the following workaround for IE:
/* All browsers */
input {
    padding: 0 7px 0 7px;
}

/* IE only */
* input {
    width: auto;
    overflow: visible;
}
By the way, the star hack (here * input) is only understood by IE and ignored by other browsers. I've tested this working trick for IE 6-8.