Client-Side Form Validation

The HTML5 specification lists several attributes we can use to validate user input on the client side, so we can catch simple input errors before the user sends the requests to the server. We’ve been able to do this for years using JavaScript, but HTML5 forms can use new attributes to specify the behavior.

We can ensure that a user has required a form field by adding the required attribute like this:

<label for=”name”>Name</label>
<input type=”text” name=”name” autofocus required id=”name”>

Browsers can then prevent the form from submitting and display a nice error message, and we don’t have to write a single line of JavaScript validation. Opera does this right now

This lets users fail early, without waiting for a server response to find out whether they made a mistake. This behavior could be disabled or unavailable or just simply not correctly implemented, so you still need to make sure you have a server-side strategy for validating data. It’s definitely something to start thinking about now, though, because you can then easily locate the required fields and style the interface with CSS so that the required fields stand out from the rest.

You can take this one step further with the pattern attribute, which lets you specify a regular expression to ensure that the content meets your criteria.

<label for=”name”>Name</label>
<input type=”text” name=”name” autofocus required id=”name”>

Although no current browser uses this through the user interface, using this markup as the basis for a JavaScript validation library would be easy to implement.

 

Native Drag-and-Drop Support

Letting users drag and drop interface elements is something we’ve been able to do with JavaScript libraries for quite a while, but the W3C has adopted Microsoft’s Drag and Drop implementation as part of the HTML5 specification.7 It’s supported by Firefox, Safari, Internet Explorer, and Chrome, but in actuality it’s a mess.

The implementation at first appears to be straightforward; we designate an element as “draggable,” we then designate an element that watches for a dropped object, and we execute some code when that happens.

In reality, it’s not nearly that simple. To demonstrate, let’s create a simple drag-and-drop interface that lets us drag small images into a drop area that will load the larger version.

<div id=”images”>
<i mg s rc=”images/red_thumb.jpg”
d a t a – l a r g e = “images/red.jpg” alt=”/A red flower”>
<i mg s rc=”images/purp1e_thumb.jpg”
data-~arge=”images/purple .jpg” alt=”/A white and purple flower”>
<i mg src=”images/white_thumb.jpg”
data-~arge=”images/white.jpg” alt=”/A white flower”>
< / d i v >
<div id=”preview”>
<p>Drop images here</p>
< / d i v >

We’re using custom data attributes here to hold the source of the larger version of our photos.

Our photo viewer

Next we’ll add some basic styles to float the two columns:

#images img{
-webki t – u s e r – d r a g
}
#images{
f l o a t :
w i d t h :
margi n
}
# p r e v i ew{
f l o a t : l e f t ;
w i d t h : 500px;
b a c k g r o u n d – c o l o r : #ddd;
h e i g h t : 335px;
}
. h o v e r {
b o r d e r : lOpx s o l i d #000;
b a c k g r o u n d – c o l o r : #bbb ¡ i m p o r t a n t ;
}

At this point, our Interface looks like the one In Figure 11.1, on the previous page. Now let’s add some events so we can drag the photos.

Drag-and-Drop Events

We’ll need to work with several events related to dragging and dropping elements.

Drag-and-Drop EventsThat’s a total of seven events just to handle dragging and dropping elements, and some of the events have default behaviors. If we don’t override them, the whole thing fails.

First, we need to define all of our list items as draggable.

var c o n t a c t s = $(‘#images i m g ‘ ) ;
c o n t a c t s . a t t r C ‘ d r a g g a b l e ‘ , ‘ t r u e ‘ ) ;

We’re adding the draggable HTML5 attribute. We could do this in our markup, but since we require JavaScript to do the interaction, we’ll apply this attribute with our script.

When we drag the image, we want to grab the address of the large Image and store it. We’ll bind to the ondragstart event, and to keep it simple and cross-platform, we’ll use jQuery’s bind() method.

Une l c o n t a c t s . b i n d ( ‘ d r a g s t a r t ‘ , f u n c t i o n ( e v e n t ) {
2 var data = e v e n t . o r i g i n a l E v e n t . d a t a T r a n s f e r ;
3 var src = $ ( t h i s ) . a t t r ( ” d a t a – 7 a r g e ” ) ;
4 data.setDataC”Text”, s r c ) ;
5 return t r u e ;
6 });

The specification provides a datastorage mechanism that lets us specify the type of data and the data itself, which is passed along as part of the event. jQuery’s bind() method wraps the event in its own object, so we use the originalevent property on 2 to access the real event. We store theURL to the image on the event by using the setData() method on line 4, using Text as the data type.

Now that we can drag elements, let’s talk about how we fire events when the user drops the elements.

Dropping Elements

We want our “To” form field to act as our drop target, so we’ll locate it and bind the drop event.

Une l var t a r g e t = $ ( ‘ # p r e v i e w ‘ ) ;
t a r g e t . b i n d C ‘ d r o p ‘ , f u n c t i o n ( e v e n t ) {
v a r data = e v e n t . o r i g i n a l E v e n t . d a t a T r a n s f e r ;
5 var src = ( d a t a . g e t D a t a ( ‘ T e x t ‘ ) );
v a r img = $ ( ” < i m g x / i m g > ” ) . a t t r ( ” s r c ” , s r c ) ;
$ ( t h i s) . h t m l (img) ;
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
io r e t u r n ( f a l se) ;
} ) ;

We retrieve the image address we passed with the event using the get- Data () method on line 5, and we then create a new image element that we push into our content region.

We need to cancel the default ondrop event so it won’t fire when our user drops the element onto the target. To do that, we need to use both preventdefault() and return false. Internet Explorer needs return false, and other browsers need preventDefault().

If we try to use this in Chrome or Safari right now, it won’t work quite right. At a minimum, we have to override the ondragover element. If we don’t, our ondrag event won’t respond. So, we’ll do that by using this code:

t a r g e t . b i n d C ‘ d r a g o v e r ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

We’re just canceling out the default event again the same way we did with the ondrop event. Let’s do the same with the ondragend event too.

c o n t a c t s . b i n d C ‘ d r a g e n d ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

This will cancel out any browser events that fire when our user stops dragging an element, but it won’t interfere with our defined ondrop event.

Changing Styles

We want to let the user know they have dragged an element over a drop target, and we can do that using the ondragenter and ondragleave methods.

c o n t a c t s . b i n d C ‘ d r a g e n d ‘ , f u n c t i o n ( e v e n t ) {
i f ( e v e n t . p r e v e n t D e f a u l t ) e v e n t . p r e v e n t D e f a u l t ( ) ;
r e t u r n f a l s e ;

This applies our hover class in our style sheet, which will be applied and removed when these events fire.

File Dragging

Moving text and elements around the page is just the beginning. The specification allows developers to create interfaces that can receive files from the user’s computer. Uploading a photo or attaching a file is as easy as dragging the file onto a specified target. In fact, Google’s Gmail supports this if you are using Firefox 3.6 or Chrome 5. If you want to explore this further, take a look at the excellent article9 by Leslie Michael Orchard.

All Is Not Well

The behavior in various browsers is, to be kind, inconsistent. IE 8 works, but it breaks if we try to set the data type for setData() to Url instead of Text.

Additionally, in order to support dragging of elements that are not images or links in Safari 4, we’d need to add additional CSS to our style sheet.

# c o n t e n t s l i {
-webki t – u s e r – d r a g
}

Throughout this book, we’ve discussed how important it is to keep style and behavior separated from content, and this flies right in the face of that concept.

Don’t try dragging text onto form fields. Modern browsers already let you do this, but there’s no good way to override that behavior.

As it stands, we can get much better results with much less code by using a JavaScript library that supports dragging and dropping like jQuery UI

Even with a library, we still have one last thing to worry about: accessibility. The specification doesn’t say anything about how to handle users who can’t use a mouse. If we implemented drag-and-drop functionality on our interfaces, we’d need to develop a secondary method that didn’t require JavaScript or a mouse to work, and that method would depend on what we’re trying to do.

This specification has a lot of potential, but it also has some things that need to be addressed. Use it if it makes sense, but ensure you don’t force your users into something they can’t use.

 

 

Talking Across Domains

Client-side web applications have always been restricted from talking directly to scripts on other domains, a restriction designed to protect users.5 There are numerous clever ways around this restriction, including the use of server-side proxies and clever URL hacks. But now there’s a better way.

The HTML5 specification introduced Cross-document Messaging, an API that makes it possible for scripts hosted on different domains to pass messages back and forth. For example, we can have a form on http://support.awesomecompany.com post content to another window or ¡frame whose content is hosted on http://www.awesomecompany.com. It turns out that for our current project, we need to do just that.

AwesomeCo’s new support site will have a contact form, and the support manager wants to list all the support contacts and their email addresses next to the contact form. The support contacts will eventually come from a content management system on another server, so we
can embed the contact list alongside the form using an ¡frame. The catch is that the support manager would love it if we could let users click a name from the contact list and have the email automatically added to our form.

We can do this quite easily, but you’ll need to use web servers to properly test everything on your own setup. The examples we’re working on here don’t work in every browser unless we use a server. See the sidebar on the following page for more on this.

The Contact List

We’ll create the contact list first. Our basic markup will look like this:

<ul id=”contacts”>
<li>
<h2>Sales</h2>
<p>James Norris</p>
<p > j . n o r r i [email protected]</p>
</li>

If you don’t want to go through the trouble of configuring Apache instances or setting up your  own servers, you can use the simple Ruby-based servers included in the book’s example
code files. For instructions on getting Ruby working on your system, see t he file RUBY_README.txt within the book’s source c o d e files.

To start t he servers, first go into t he html5xdomain/contactlist a n d run the server.rb file like this:

ruby s e r v e r . rb

It will start on port 4567. You can then do the same for the server.rb in html5xdomain/supportpage, which will start on port 3000. You can edit the port for each of these by editing the server.rb file.

<li>
<h2>Operations</h2>
<p>Tony Raymond</p>
<p>t. [email protected] com</p>
</li>
<li>
<h2>Accounts Payable</h2>
<p>Clark Greenwood</p>
<p>c [email protected] com</p>
</li>
<li>
<h2>Accounts Receivable</h2>
<p>Herbert Whitmore</p>
<p>[email protected]</p>
</l i>
< / u l >

On that page, we’ll also load both the jQuery library and our own custom application.js file and a simple style sheet. We’ll place this in our head section:

< s c r i p t type=”text/javascript”
charset= “utf-8″
src=”http://ajax.googleapis.com/ajax/1ibs/jquery/1.4.2/jquery.min.js”>
</script>

< s c r i p t t y p e = ” t e x t /javascript”
src=”javascripts/application.js”>
</script>
<link rel=”stylesheet” href=”style.css” type=”text/css” media=”screen”>

The style sheet for the contact list looks like this:

Ul{
l i s t – s t y l e : none;
}
ul h2, ul p{margin: 0 ; }
ul l i { m a r g i n – b o t t o m : 20px;}

It’s just a couple of small tweaks to make the list look a little cleaner.

Posting the Message

When a user clicks an entry in our contact list, we’ll grab the email from the list item and post a message back to the parent window. The postMessage() method takes two parameters: the message itself and the target window’s origin. Here’s how the entire event handler looks:

$ ( f u n c t i o n ( ) {
$(“^contacts li”).click(function(event){
var email = ( $ ( t h i s ) . f i n d ( ” . e m a i 7 ” ) .html ( ) ) ;
var o r i g i n = “http://192.168.1.244:3000/index.html”;
window.parent.postMessage(email , o r i g i n ) ;
} ) ;

You’ll need to change the origin if you’re following along, since it has to match the URL of the parent window.

Now we need to implement the page that will hold this frame and receive its messages.

The Support Site

The support site’s structure is going to look very similar, but to keep things separate, we should work in a different folder, especially since this site will need to be placed on a different web server. We’ll need to make sure you include links to a style sheet, jQuery, and a new
application.js file.

Our support page needs a contact form and an ¡frame that points to our contact list. We’ll do something like this:

<div id=”form”>
<form id=”supportform”>
<fieldset>
<ol>
<li>
<label for=”to”>To</label >
<input type=”email” name=”to” id=”to”>
</li>
< l i >
<label for=”from”>From</label>
<input type=”text” name=”from” id=”from”>
</li>
< l i >
<1abel for=”message”>Message</label>
<textarea name=”message” id=”message”x/textarea>
< / l i >
</ol>
<input type=”submit” value=”Send!”>
</fieldset>
</form>
< / d i v >
<div id=”contacts”>
<i frame src=”http://192.168.1.244:4567/index.html”x/i frame>
< / d i v >

We’ll style it up with this CSS that we add to style.ess:

#form{
w i d t h : 400px;
f l o a t : l e f t ;
}
#contacts{
w i d t h : 200px;
f l o a t : l e f t ;
}
#contacts iframe{
border: none;
h e i g h t : 400px;
}

f i e l d s e t {
w i d t h : 400px;
b o r d e r : none;
f i e l d s e t legend{
b a c k g r o u n d – c o l o r : #ddd;
p a d d i n g : 0 64px 0 2px;
}
f i e l d s e t > o l {
l i s t – s t y l e : none;
p a d d i n g : 0;
m a r g i n : 2px;
}
f i e l d s e t > o l > l i {
m a r g i n : 0 0 9px 0;
p a d d i n g : 0;
}
/ * Make i n p u t s go to t h e i r own l i n e */
f i e l d s e t i n p u t , f i e l d s e t t e x t a r e a {
d i s p l a y : b l o c k ;
w i d t h : 380px;
}
f i e l d s e t i n p u t [ t y p e = s u b m i t ] {
w i d t h : 390px;
f i e l d s e t t e x t a r e a {
h e i g h t : lOOpx;
}

This places the form and the ¡frame side by side and modifies the form so it looks like Figure 10.1, on the previous page.

Receiving the Messages

The onmessage event fires whenever the current window receives a message. The message comes back as a property of the event. We’ll register this event using jQuery’s bind() method so it works the same in all browsers.

$ (function (){
$(wi ndow).bi nd(“message”,function( e v e n t ) {
$ ( ” # t o ” ) .val ( e v e n t , o r i g i n a l Event, d a t a ) ;
} ) ;
} ) ;

jQuery’s bind() method wraps the event and doesn’t expose every property. We can get what we need by accessing it through the event’s originalEvent property instead.

If you open this in Firefox, Chrome, Safari, or Internet Explorer 8, you’ll see that it works extremely well. Now let’s make it work for IE 6 and 7.

Falling Back

To support IE 6 and 7, we’ll use the jQuery Postback plug-in, which emulates cross-domain messaging. We’ll use jQuery’s getScript() method to pull that library in only when we need it. To do that, we’ll just detect whether the postMessage() method exists.

First, we’ll modify our contact list.

i f ( w i ndow.postMessage){
w i n d o w . p a r e n t . p o s t M e s s a g e ( e m a i l , o r i g i n ) ;
} e l s e {
$ . g e t S c r i p t ( “javascripts/jquery.postmessage.js”, f u n c t i o n O {
$.postMessage(email, o r i g i n , w i n d o w . p a r e n t ) ;
} ) ;
}

The jQuery Postmessage plug-in adds a postMessage() method, which works almost exactly like the standard postMessage() method.

Now, let’s turn our attention to the support site. We’ll use the same approach here, pulling in the library and calling the newly added receiveMessage() method.

i f(window.postMessage){
$(wi ndow).bi nd(“message”,function(event){
$ ( ” # t o ” ) .val ( e v e n t . o r i g i n a l E v e n t . d a t a ) ;
} ) ;
}else{
$.getScript(“javascripts/jquery.postmessage.js”, functionO{
$.receiveMessage(
function(event){
$ ( ” # t o ” ) . v a l ( e v e n t . d a t a ) ;
} ) ;
} ) ;
}

That’s it! We can now talk across windows in a whole bunch of browsers. This is just the beginning, though; you can expand this technique to do two-way communication, too. Any window can be a sender or a receiver, so take a look at the specification and see what you can build!

 

 

Saving Preferences with localStorage

The localStorage mechanism provides a very simple method for developers to persist data on the client’s machine. The localStorage mechanism Is simply a name/value store built In to the web browser.

Information stored In localStorage persists between browser sessions and can’t be read by other websites, because It’s restricted to the domain you’re currently visiting

AwesomeCo Is In the process of developing a new customer service portal and wants users to be able to change the text size, background, and text color of the site. Let’s Implement that using localStorage so that when we save the changes, they persist from one browser session to the next. When we’re done, we’ll end up with a prototype that looks like Figure 9.1, on the following page.

Building the Preferences Form

Let’s craft a form using some semantic HTML5 markup and some of the new form controls you learned about In Chapter 3, Creating User- Friendly Web Forms, on page 45. We want to let the user change the foreground color, change the background color, and adjust their font size.

<pxstrong>Preferences</strongx/p>
<form id= “preferences” action =”save_prefs”
method=”post” accept-charset=”utf-8″>
<fieldset id=”colors” class=””>
<1egend>Colo rs</legencb
<ol>
< l i >
<label for=”background_color”>Background color</label>
<input type=”co7or” name=”background_color”
v a l u e = ” ” id= “background_color”>
</li>

<li>
<label for=”text_color”>Text color</label>
<input type=”co7or” name=”text_color”
v a l u e = ” ” id=”text_color”>
</li>
<li>
<label for=”text_size”>Text size</label>
<select name=”text_size” id=”text_size”>
<option value=”16″>16px</option>
<option value=”20″>20px</option>
<option value=”24″>24px</option>
<option value=”32″>32px</option>
</select>
</ol>
</fieldset>
<input type=”submit” value=”Save changes”»
</form>

We’ll just use HTML color codes for the color.

Saving and Loading the Settings

To work with the localStorage system, you use JavaScript to access the window.localStorageO object. Setting a name and value pair is as simple as this:

l o c a l Storage . setItem(“background_color” , $(“#background_color”) .val O) ;

Grabbing a value back out is just as easy.

var bgcolor = 1ocalStorage.getltemC”background_color”);

Let’s create a method for saving all the settings from the form.

f u n c t i o n s a v e _ s e t t i n g s ( ) {
l o c a l Storage.setltemC”background_color”, $(“#background_color”).val());
l o c a l S t o r a g e . s e t l t e m C”text_color”, $ ( ” # t e x t _ c o 7 o r ” ) . v a l ( ) ) ;
l o c a l Storage.setltemC”text_size”, $(“#text_size”).val());
a p p l y _ p r e f e r e n c e s _ t o _ p a g e ( ) ;
}

Next, let’s build a similar method that will load the data from the local- Storage system and place it into the form fields.

f u n c t i o n l o a d _ s e t t i n g s ( ) {
var bgcolor = 1ocalStorage.getltemC”background_color”) ;
var t e x t _ c o l o r = l o c a l S t o r a g e . g e t l t e m C ” t e x t _ c o 7 o r ” ) ;
var t e x t _ s i z e = 1ocalStorage.getltemC”text_size”) ;
$C’#background_color”) .val Cbgcolor) ;
$ C ” # t e x t _ c o 7 o r ” ) . v a l C t e x t _ c o l o r ) ;
$(“#text_size”).valCtext_size);
a p p l y _ p r e f e r e n c e s _ t o _ p a g e O ;
}

This method also calls a method that will apply the settings to the page itself, which we’ll write next.

Applying the Settings

Now that we can retrieve the settings from localStorage, we need to apply them to the page. The preferences we’re working with are all related to CSS in some way, and we can use jQuery to modify any element’s styles.

f u n c t i o n a p p l y _ p r e f e r e n c e s _ t o _ p a g e O{
SC’body”) .cssC’backgroundColor” , $C’#background_color”) .val O) ;
$C’body”) . c s s C ” c o 7 o r ” , $ C ” # t e x t _ c o 7 o r ” ) . v a l O) ;
SC’body”) .cssC’fontSize” , $C’#text_size”) . v a l O + “px”) ;
}

Finally, we need to fire all of this when the document is ready.

$ ( f u n c t i o n O {
l o a d _ s e t t i n g s O ;
$ C ‘ f o r m # p r e f e r e n c e s ‘ ) . s u b m i t ( f u n c t i o n ( e v e n t ) {
e v e n t . p r e v e n t D e f a u l t ( ) ;
s a v e _ s e t t i n g s O ;
} ) ;

Falling Back

The localStorage method works only on the latest Internet Explorer, Firefox, Chrome, and Safari, so we’ll need a fallback method for older browsers. We have a couple of approaches.  We can save the information on the server, or we persist the preferences on the client side using cookies.

Server-Side Storage

If you have user accounts in your system, consider making the preferences page persist the settings to the user’s record in your application. When they log in, you can check to see whether any client-side settings exist and, if they don’t, load them from the server. This way, your users keep their settings across browsers and across computers.

To persist to the server, simply ensure your form posts to the server—don’t prevent the default submit behavior with JavaScript if there’s no support for cookies.

Server-side storage is really the only method that will work if the user disables JavaScript, because you could code your application to fetch the settings from the database and not the localStorage hash. Also, this is the only approach you can take if you’re storing more than 4KB of data, since that’s the maximum amount of data you can store in a cookie.

Cookies and JavaScript

The tried-and-true combination of cookies and JavaScript can act as a decent fallback. Using the well-known cookie script from Quirksmode,5 we can build our own localStorage fallback solution.

Detecting localStorage support In the browser Is pretty simple. We just check for the existence of a localStorage method on the window object:

i f ( ! w i n d o w . l o c a l S t o r a g e ){
}
Next, we need methods to write the cookies, which we’ll borrow from
the Quirksmode article. Add these JavaScript functions to your script
block, within the braces:

f u n c t i o n c r e a t e C o o k i e C n a m e , v a l u e , d a y s ) {
i f (days) {
v a r date = new D a t e O ;
d a t e . s e t T i m e ( d a t e . g e t T i m e ( ) + ( d a y s * 2 4 * 6 0 * 6 0 * 1 0 0 0 ) ) ;
v a r e x p i r e s = “; e x p i r e s = ” + d a t e . t o G M T S t r i n g C ) ;
}
e l s e var e x p i r e s = ” ” ;
d o c u m e n t . c o o k i e = n a m e + ” = ” + v a l u e + e x p i r e s + ” ; path=/”;
}
f u n c t i o n readCookie(name) {
v a r r e s u l t = “”
v a r nameEQ = name + ” = ” ;
v a r ca = d o c u m e n t . c o o k i e . s p l i t C ; ‘ ) ;
f o r C v a r i = 0 ; i < c a . l e n g t h ; i + + ) {
v a r c = c a [ i ] ;
w h i l e ( c . c h a r A t ( 0 ) = = ‘ ‘) c = c . s u b s t r i n g ( l , c . l e n g t h ) ;
i f (c.indexOf(nameEQ) == 0 ) {
r e s u l t = c . s u b s t r i n g C n a m e E Q . l e n g t h , c . l e n g t h ) ;
} e l s e {
r e s u l t = “” ;
}
}
r e t u r n ( r e s u l t ) ;
}

Finally, we want to make a localStorage object that uses the cookies as Its back end. A very hackish example that just barely makes this work might look like this:

Une l l o c a l S t o r a g e = ( f u n c t i o n () {
r e t u r n {
s e t l t e m : f u n c t i o n (key, v a l u e ) {
c r e a t e C o o k i e ( k e y , v a l u e , 3000)
5 },

We can use localStorage for things that we want to persist even after our users close their web browsers, but sometimes we need a way to store some information while the browser is open and throw it away once the session is over. That’s where sessionStorage comes into play. It works the same way as localStorage, but the contents of the sessionStorage are cleared out once the browser session ends. Instead of grabbing the localStorage object, you grab the sessionStorage object.

s e s s i o n S t o r a g e . s e t l t e m C ‘ n a m e ‘ , ‘Brian Hogan’);
var name = s e s s i o n S t o r a g e . g e t l t e m C ‘name ‘) ;

Creating a fallback solution for this is as simple as ensuring that the cookies you create expire when the browser closes.

g e t l t e m : f u n c t i o n (key) {
r e t u r n ( r e a d C o o k i e ( k e y ) ) ;
}
};
} )  ;

Take note of line 4. We’re creating a cookie with an expiration date of 3,000 days from now. We can’t create cookies that never expire, so I’m setting this to a ridiculously long time into the future.

We’ve kept the basic implementation of localStorage the same from the outside. If you need to remove items or clear everything out, you’ll need to get a little more creative. Ideally, in the near future, we can remove this hackish solution and rely only on the browser’s localStorage()
methods.

 

 

Using Real Fonts

Typography is so important to user experience. The book you’re reading
right now has fonts that were carefully selected by people who understand
how choosing the right fonts and the right spacing can make it
much easier for people to read this book. These concepts are just as
important to understand on the Web.

The fonts we choose when conveying our message to our readers impact
how our readers interpret that message. Here’s a font that’s perfectly
appropriate for a loud heavy-metal band:

But that might not work out so well for the font on the cover of this
book:

As you can see, choosing a font that matches your message is really
important. The problem with fonts on the Web is that we web developers
have been limited to a handful of fonts, commonly known as “websafe”
fonts. These are the fonts that are in wide use across most users’
operating systems.

To get around that, we’ve historically used images for our fonts and
either directly added them to our page’s markup or used other methods
like CSS background images or sIFR,8 which renders fonts using Flash.
CSS3’s Fonts module offers a much nicer approach.

@font-face

The @font-face directive was actually introduced as part of the CSS2
specification and was implemented in Internet Explorer 5. However,

Fonts and Rights

Some fonts aren’t free. Like stock photography or other copyrighted
material, you are expected to comply with the rights
and licenses of the material you use on your website. If you
purchase a font, you’re usually within your rights to use it in
your logo and images on your pages. These are called usage
rights. However, the @font-face approach brings a different kind
of licensing into play—redistribution rights.
When you embed a font on your page, your users will have
to download that font, meaning your site is now distributing
this font to others. You need to be absolutely positive the fonts
you’re using on your pages allow for this type of usage.
Typekit* has a large library of licensed fonts available, and they
provide tools and code that make it easy to integrate with your
website. They are not a free service, but they are quite affordable
if you need to use a specific font.
Google provides the Google Font APIf, which is similar to Typekit
but contains only open source fonts.
Both of these services use JavaScript to load the fonts, so you
will need to ensure that your content is easy to read for users
without JavaScript.
As long as you remember to treat fonts like any other asset, you
shouldn’t run into any problems.

Microsoft’s implementation used a font format called Embedded Open-
Type (EOT), and most fonts today are in TrueType or OpenType format.
Other browsers support the OpenType and TrueType fonts currently.
AwesomeCo’s director of marketing has decided that the company
should standardize on a font for both print and the Web. You’ve been
asked to investigate a font called Garogier, a simple, thin font that is
completely free for commercial use. As a trial run, we’ll apply this font
to the blog example we created in Redefining a Blog Using Semantic
Markup, on page 27. That way, everyone can see the font in action.

Font Formats

Fonts are available in a variety of formats, and the browsers you’re targeting
will determine what format you’ll need to serve to your visitors.
Format and Supported Browsers

Embedded OpenType (EOT) [IE5-8]
TrueType (TTF) [IE9, F3.5, C4, S4]
OpenType (OTF) [IE9, F3.5, C4, S4, 010.5J
Scalable Vector Graphics (SVG) [IOSJ
Web Open Font (WOFF) [IE9, F3.6]

Internet Explorer browsers prior to 9 only support a format called
Embedded OpenType (EOT). Other browsers support the more common
TrueType and OpenType fonts quite well.
Microsoft, Opera, and Mozilla jointly created the Web Open Font Format,
which allows lossless compression and better licensing options for
font makers.
To hit all of these browsers, you have to make your fonts available in
multiple formats.

Changing Our Font

The font we’re looking at is available at FontSquirrel9 in TrueType,
WOFF, SVG, and EOT formats, which will work just perfectly.
Using the font involves two steps—defining the font and attaching the
font to elements. In the style sheet for the blog, add this code:

@ f o n t – f a c e {
f o n t – f a m i l y : ‘ G a r o g i e r R e g u l a r ‘ ;
s r c : u r l ( ‘ f o n t s / G a r o g i e r _ u n h i n t e d – w e b f o n t . e o t ‘ ) ;
s r c : u r l ( ‘ f o n t s / G a r o g i e r _ u n h i n t e d – w e b f o n t . w o f f ‘ ) f o r m a t C ‘ w o f f ‘ ) ,
u r l C ‘ f o n t s / G a r o g i e r _ u n h i n t e d – w e b f o n t . t t f ‘ ) f o r m a t ( ‘ t r u e t y p e ‘ ) ,
u r l ( ‘ f o n t s / G a r o g i e r _ u n h i n t e d – w e b f o n t . s v g # w e b f o n t e w 0 q E 0 0 9 ‘ ) f o r m a t ( ‘ s v g ‘ ) ;
f o n t – w e i g h t : normal;
}

We’re defining the font family first, giving it a name, and then supplying
the font sources. We’re putting the Embedded OpenType version first
so that IE sees it right away, and then we provide the other sources.
A user’s browser is going to just keep trying sources until it finds one
that works.
Now that we’ve defined the font family, we can use it in our style sheet.
We’ll change our original font style so it looks like this:

body{
f o n t – f a m i l y : ” G a r o g i e r R e g u l a r “;
}

With that simple change, our page’s text displays in the new font, like
the example in Figure 8.6, on the next page.
Applying a font is relatively easy in modern browsers, but we need to
consider browsers that don’t support this yet.

Falling Back
We’ve already provided fallbacks for various versions of IE and other
browsers, but we still need to ensure our pages are readable in browsers
that lack support for the @font-face feature.
We provided alternate versions of the Garogier font, but when we applied
the font, we didn’t specify any fallback fonts. That means if the
browser doesn’t support displaying our Garogier font, it’s just going to
use the browser’s default font. That might not be ideal.
Font stacks are lists of fonts ordered by priority. You specify the font
you really want your users to see first and then specify other fonts that
are suitable fallbacks afterwards.
When creating a font stack, take the extra time to find truly suitable
fallback fonts. Letter spacing, stroke width, and general appearance
should be similar. The website Unitlnteractive has an excellent article
on this.10
Let’s alter our font like this:

f o n t – f a m i l y : ” G a r o g i e r R e g u l a r ” , Georgia,
” P a l a t i n o ” , ” P a l a t i n o L i n o t y p e ” ,
” T i m e s ” , “Times New Roman”, s e r i f ;

We’re providing a large array of fallbacks here, which should help us
maintain a similar appearance. It’s not perfect in all cases, but it’s better
than relying on the default font, which can sometimes be quite hard
to read.
Fonts can go a long way to make your page more attractive and easier
to read. Experiment with your own work. There are a large number of
fonts, both free and commercial, waiting for you.

The Future
In this chapter, we explored a few ways CSS3 replaces traditional web
development techniques, but we only scratched the surface. The CSS3
specification talks about 3D transformations and even simple animations,
meaning that we can use style sheets instead of JavaScript to
provide interaction cues to users, much like we do with :hover.
In addition, some browsers are already supporting multiple background
images and gradient borders. Finally, keep an eye out for improvements
in paged content, such as running headers and footers and page number
support.
The CSS3 modules, when completed, will make it much easier for us to
create richer, better, and more inviting interface elements for our users,
so be sure to keep an eye out for new features.

Working with Shadows, Gradients, and Transformations

While rounded corners get a lot of attention, that’s just the beginning
of what we can do with CSS3. We can add drop shadows to elements to
make them stand out from the rest of the content, we can use gradients
to make backgrounds look more defined, and we can use transformations
to rotate elements. Let’s put several of these techniques together
to mock up a banner for the upcoming AwesomeConf, a trade show and
conference that AwesomeCo puts on each year. The graphic designer
has sent over a PSD that looks like Figure 8.3, on the next page. We
can do the badge, shadow, and even the transparency all in CSS. The
only thing we’ll need from the graphic designer is the background image
of the people.

The Basic Structure

Let’s start by marking up the basic structure of the page in HTML.

<div id=”conference”>
<section id=”badge”>
<h3>Hi, My Name Is</h3>
<h2>Barney</h2>
</section>
<section id=”info”>
</section>
</div>

We can style the basics with this:

# c o n f e r e n c e {
b a c k g r o u n d – c o l o r : #000;
w i d t h : 960px;
f l o a t : l e f t ;
background-image: u r l ( ‘ i m a g e s / a w e s o m e c o n f . j p g ‘ );
b a c k g r o u n d – p o s i t i o n : c e n t e r;
h e i g h t : 240px;
}

re-create using CSS3

#badge{
t e x t – a l i g n : c e n t e r ;
w i d t h : 200px;
b o r d e r : 2px s o l i d b l u e ;
}
# i n f o {
m a r g i n : 20px;
p a d d i n g : 20px;
w i d t h : 660px;
h e i g h t : 160px;
}
#badge, # i n f o {
f l o a t : l e f t ;
b a c k g r o u n d – c o l o r : #fff;
}
#badge h2{
m a r g i n : 0;
c o l o r : red;
f o n t – s i z e : 40px;
}
#badge h3{
m a r g i n : 0;
b a c k g r o u n d – c o l o r : blue;
c o l o r : #fff;
}

Once we apply that style sheet to our page, we have our badge and
content region displayed side-by-side, as shown in Figure 8.4, on the
following page, so let’s start styling the badge.

Adding a Gradient

We can add definition to the badge by changing the white background
to a subtle gradient that goes from white to light gray. This gradient
will work in Firefox, Safari, and Chrome, but the implementation is
different for Firefox. Chrome and Safari use WebKit’s syntax, which
was the original proposal, whereas Firefox uses a syntax that’s close to
the W3C proposal. Once again, we’re using browser prefixes

#badge{
b a c k g r o u n d – i m a g e : – m o z – 1 i n e a r – g r a d i e n t (
t o p , # f f f , # e f e f e f
);
b a c k g r o u n d – i m a g e : – w e b k i t – g r a d i e n t (
l i n e a r , l e f t t o p , l e f t b o t t o m ,
c o l o r – s t o p C O , # f f f ) ,
c o l o r – s t o p C l , # e f e f e f )
);
b a c k g r o u n d – i m a g e : 1i n e a r – g r a d i e n t (
t o p , # f f f , # e f e f e f
);

Firefox uses the -moz-linear-gradient method, in which we specify the
starting point of the gradient, followed by the starting color, and, finally,
the ending color.

WebKit-based browsers let us set color stops. In our example, we only
need to go from white to gray, but if we needed to add colors, we’d just
need to add an additional color stop in the definition.

Adding a Shadow to the Badge

We can easily make the badge appear to be sitting above the banner by
adding a drop shadow. Traditionally, we’d do this shadow in Photoshop
by adding it to the image or by inserting it as a background image.
However, the CSS3 box-shadow property lets us quickly define a shadow
on our elements.

We’ll apply this rule to our style sheet to give the badge a shadow:

#badge{
-moz-box-shadow: 5px 5px 5px #333;
– w e b k i t – b o x – s h a d o w : 5px 5px 5px #333;
– o – b o x – s h a d o w : 5px 5px 5px #333;
box-shadow: 5px 5px 5px #333;
}

The box-shadow property has four parameters. The first is the horizontal
offset. A positive number means the shadow will fall to the right of the
object; a negative number means it falls to the left. The second parameter
is the vertical offset. With the vertical offset, positive numbers make
the shadow appear below the box, whereas negative values make the
shadow appear above the element.

The third parameter is the blur radius. A value of 0 gives a very sharp
value, and a higher value makes the shadow blurrier. The final parameter
defines the color of the shadow.

You should experiment with these values to get a feel for how they work
and to find values that look appropriate to you. When working with
shadows, you should take a moment to investigate how shadows work
in the physical world. Grab a flashlight and shine it on objects, or go
outside and observe how the sun casts shadows on objects. This use
of perspective is important, because creating inconsistent shadows can
make your interface more confusing, especially if you apply shadows to
multiple elements incorrectly. The easiest approach you can take is to
use the same settings for each shadow you create.

Rotating the Badge

You can use CSS3 transformations to rotate, scale, and skew elements
much like you can with vector graphics programs such as Flash, Illustrator,
or Inkscape.6 This can help make elements stand out a bit more
and Is another way to make a web page not look so “boxy.” Let’s rotate
the badge just a bit so It breaks out of the straight edge of the banner.

#badge{
– m o z – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– o – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– w e b k i t – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
– m s – t r a n s f o r m : r o t a t e ( – 7 . 5 d e g ) ;
t r a n s f o r m : r o t a t e ( – 7 . 5deg) ;
}

Rotation with CSS3 Is pretty simple. All we have to do Is provide the
degree of rotation, and the rendering just works. All the elements contained
within the element we rotate are rotated as well.
Rotating Is just as easy as rounding corners, but don’t overuse It. The
goal of Interface design Is to make the Interface usable. If you rotate
elements containing a lot of content, ensure that your viewers can read
the content without turning their heads too far In one direction!

Transparent Backgrounds

Graphic designers have used semitransparent layers behind text for
quite some time, and that process usually involves either making a
complete image in Photoshop or layering a transparent PNG on top of
another element with CSS. CSS3 lets us define background colors with
a new syntax that supports transparency.

When you first learn about web development, you learn to define your
colors using hexadecimal color codes. You define the amount of red,
green, and blue using pairs of numbers. 00 is “all off’ or “none,” and FF
is “all on.” So, the color red would be FF0000 or “all on for red, all off for
blue, and all off for green.”

CSS3 introduces the rgb and rgba functions. The rgb function works
like the hexadecimal counterpart, but you use values from 0 to 255 for
each color. You’d define the color red as rgb(255,0,0).

The rgba function works the same way as the rgb function, but it takes
a fourth parameter to define the amount of opacity, from 0 to 1. If you
use 0, you’ll see no color at all, because it’s completely transparent. To
make the white box semitransparent, we’ll add this style rule:

# i n f o {
b a c k g r o u n d – c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 9 5 ) ;
}

When working with transparency values like this, your users’ contrast
settings can sometimes impact the resulting appearance, so be sure to
experiment with the value and check on multiple displays to ensure
you get a consistent result.

While we’re working with the info section of our banner, let’s round the
corners a bit.

# i n f o {
m o z – b o r d e r – r a d i u s : 12px;
w e b k i t – b o r d e r – r a d i u s : 12px;
o – b o r d e r – r a d i u s : 12px;
b o r d e r – r a d i u s : 12px;
}

With that, our banner looks pretty good in Safari, Firefox, and Chrome.
Now let’s implement a style sheet for Internet Explorer.

Falling Back

The techniques we used in this section work fine in IE 9, but they’re
all possible with Internet Explorer 6, 7, and 8 too! We just have to use
Microsoft’s DirectX filters to pull them off. That means we’ll want to
rely on a conditional comment to load a specific IE-only style sheet.
We’ll also need to use JavaScript to create the section element so we
can style it with CSS since these versions of IE don’t recognize that
element natively.

<! — [ i f l t e IE 8]>
<script>
document.createElement(“section”) ;
</script>
<link rel=”stylesheet” href=”ie.css” type=”text/css” media=”screen”>
< ! [ e n d i f ] – – >
</head>
<body>
<div id=”conference”>
<section id=”badge”>
<h3>Hi, My Name Is</h3>
<h2>Barney</h2>
</section>
<section id=”info”>
</section>
</div>
</body>
</html>

The DirectX filters work in IE 6, 7, and 8, but in IE 8 the filters are
invoked differently, so you’ll be declaring each of these filters twice.
Let’s start by looking at how we rotate elements.

Rotation

We can rotate elements using these filters, but it’s not as easy as just
specifying a degree of rotation. To get the effect we want, we need to

use the Matrix filter and specify cosines and sines of the angle we want.
Specifically, we need to pass the cosine, the negative value of sine, the
sine, and the cosine again,7 like this:

f i l t e r : p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x(
s i z i n g M e t h o d = ‘ a u t o expand’,
M11=0.9914448613738104,
M12=0.13052619222005157,
M21=-0.13052619222005157,
M22=0.9914448613738104
);
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x(
s i z i n g M e t h o d = ‘ a u t o expand’,
M11=0.9914448613738104,
M12=0.13052619222005157,
M21=-0.13052619222005157,
M22=0.9914448613738104
) ” ;

Complicated? Yes, and more so when you look at the previous example
more closely. Remember that our original angle was negative 7.5
degrees. So, for our negative sine, we need a positive value, and our
sine gets a negative value.

Math is hard. Let’s make gradients instead.

Gradients

IE’s Gradient filter works just like the one in the standard, except that
you have to type a lot more characters. You provide the starting color
and the ending color, and the gradient just shows up.

f i I t e r : progi d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF
);
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF

Unlike the other browsers, you’re applying the gradient directly to the
element, rather than to the background-image property.

Let’s use this filter again to define the transparent background for our
info section.

Transparency

The Gradient filter can take extended hexadecimal values for the start
and end colors, using the first two digits to define the amount of transparency.
We can get very close to the effect we want with this code:

background: none;
f i l t e r :
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # B B F F F F F F , endColorStr=#BBFFFFFF
– m s – f i l t e r : ” p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = ‘ # B B F F F F F F ‘ , EndColorStr=’#BBFFFFFF’

These eight-digit hex codes work very much like the rgba function,
except that the transparency value comes first rather than last. So,
we’re really looking at alpha, red, green, and blue.
We have to remove the background properties on that element to make
this work in IE 7. Now, if you’ve been following along trying to build this
style sheet up, you’ve noticed that it doesn’t actually work yet, but we
can fix that.

Putting It All Together

One of the more difficult problems with these IE filters is that we can’t
define them in pieces. To apply multiple filters to a single element, we
have to define the filters as a comma-separated list. Here’s what the
actual IE style sheet looks like:

# i n f o {
background: none;
f i l t e r :
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # B B F F F F F F , endColorStr=#BBFFFFFF
);
– m s – f i l t e r : ” p r o g i d ¡ D X I m a g e T r a n s f o r m . M i c r o s o f t . g r a d i e n t(
s t a r t C o l o r S t r = ‘ # B B F F F F F F ‘ , EndColorStr=’#BBFFFFFF’
) ” ;

#badge{
f i l t e r :
p r o g i d : D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i x C
s i z i n g M e t h o d = ‘ a u t o expand’,
M11=0.9914448613738104,
M12=0.13052619222005157,
M21=-0.13052619222005157,
M22=0.9914448613738104
),
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF ),
progid:DXImageTransform.Mi c r o s o f t . S h a d o wC
c o l o r = # 3 3 3 3 3 3 , D i r e c t i o n = 1 3 5 , Strength=3
);
– m s – f i l t e r : ” p r o g i d ¡ D X I m a g e T r a n s f o r m . M i c r o s o f t . M a t r i xC
s i z i n g M e t h o d = ‘ a u t o expand’,
M11=0.9914448613738104,
M12=0.13052619222005157,
M21=-0.13052619222005157,
M22=0.9914448613738104
),
p r o g i d:DXImageTransform.Mi c r o s o f t . g r a d i e n t (
s t a r t C o l o r S t r = # F F F F F F , endColorStr=#EFEFEF ),
progid:DXImageTransform.Mi c r o s o f t . S h a d o wC
c o l o r = # 3 3 3 3 3 3 , D i r e c t i o n = 1 3 5 , Strength=3
) ” ;

That’s a lot of code to get the desired result, but it shows that it is
possible to use these features. If you look at Figure 8.5, you’ll see we
got pretty close. All we have to do now is round the corners on the infosection, and you can refer to Rounding Rough Edges, on page 146 to
see how to do that.
Although these filters are clunky and a little bit quirky, you should still
investigate them further in your own projects because you’ll be able to
provide a similar user experience to your IE users.
Remember that the effects we explored in this section are all presentational.
When we created the initial style sheet, we made sure to apply
background colors so that text would be readable. Browsers that cannot
understand the CSS3 syntax can still display the page in a readable
manner.

 

 

 

Rounding Rough Edges

On the Web, everything is a rectangle by default. Form fields, tables,
and even sections of web pages all have a blocky, sharp-edged look, so
many designers have turned to different techniques over the years to
add rounded corners to these elements to soften up the interface a bit.
CSS3 has support for easily rounding corners, and Firefox and Safari
have supported this for quite a long time. Unfortunately, Internet Explorer
hasn’t jumped on board yet. But we can get around that simply
enough.

Softening Up a Login Form

The wireframes and mock-ups you received for your current project
show form fields with rounded corners. Let’s round those corners using
only CSS3 first. Our goal is to create something that looks like Figure
8.1, on the next page.
For the login form, we’ll use some very simple HTML.

< f o rm a c t i o n = ” / l o g i n ” method=”post”>
< f i e l d s e t i d = ” l o g i n ” >
<legend>Log i n < / l e g e n d>
< o l >
< l i >
< l a b e l for=”emai 1 “>Emai l < / l a b e l >
< i n p u t i d = ” e m a i l ” t y p e = ” e m a i l ” name=”emai1″>
</li>
< l i >
< l a b e l f o r = ” p a s s w o r d ” > P a s s w o r d < / l a b e l>
< i n p u t i d = ” p a s s w o r d ” type=”password”
name=”password” v a l u e = ” ” a u t o c o m p l e t e = ” o f f ” / >
</li>
< l i x i n p u t t y p e = ” s u b m i t ” value=”Log i n ” x / l i >
</ol >
< / f i e l d s e t >
< / f o r m >

We’ll style the form a bit to give It a slightly better look.

f i e l d s e t {
w i d t h : 216px;
b o r d e r : none;
b a c k g r o u n d – c o l o r : #ddd;
}
f i e l d s e t legend{
b a c k g r o u n d – c o l o r : #ddd;
p a d d i n g : 0 64px 0 2px;
}
f i e l d s e t > o l { l i s t – s t y l e : none;
p a d d i n g : 0 ;
m a r g i n : 2px;
}
f i e l d s e t > o l > l i {
m a r g i n : 0 0 9px 0;
p a d d i n g : 0;
}
/ * Make i n p u t s go to t h e i r own l i n e */
f i e l d s e t i n p u t {
d i s p l a y : b l o c k ;
}

i n p u t {
w i d t h : 200px;
b a c k g r o u n d – c o l o r : #fff;
b o r d e r : lpx s o l i d #bbb;
i n p u t [ t y p e = ” s u b m i t ” ] {
w i d t h : 202px;
p a d d i n g : 0;
b a c k g r o u n d – c o l o r : #bbb;
}

These basic styles remove the bullets from the list and ensure that the
input fields are all the same size. With that in place, we can apply the
rounding effects to our elements.

Browser-Specific Selectors

Since the CSS3 specification isn’t final, browser makers have added
some features themselves and have decided to prefix their own implementations.
These prefixes let browser makers introduce features early
before they become part of a final specification, and since they don’t
follow the actual specification, the browser makers can implement the
actual specification while keeping their own implementation as well.
Most of the time, the vendor-prefixed version matches the CSS specification,
but occasionally you’ll encounter differences. Unfortunately for
you, that means you’ll need to declare the border radius once for each
type of browser.
Firefox uses this selector:

– m o z – b o r d e r – r a d i u s : 5px;

WebKit-based browsers, such as Safari and Chrome, use this selector:

– w e b k i t – b o r d e r – r a d i u s : 5px;

To round all the input fields on our form, we need a CSS rule like this:

i n p u t , f i e l d s e t , legend{
b o r d e r – r a d i u s : 5px;
– m o z – b o r d e r – r a d i u s : 5px;
– w e b k i t – b o r d e r – r a d i u s : 5px;

Add that to your style.ess file, and you have rounded corners.

Falling Back

You have everything working in Firefox, Safari, and Google Chrome, but
you know it doesn’t work in Internet Explorer and you know it needs to,
so you’ll need to implement something that gets it as close as possible.
Web developers have been rounding corners for a while now using background
images and other techniques, but we’re going to keep it as simple
as possible. We can detect corner radius with JavaScript and round
the corners using any number of rounding techniques. For this example,
we’ll use j Query, the j Query Corner plug-in, and a modification of
the Corner plug-in that rounds text fields.

Detecting Rounded Corners Support

Our fallback solution looks very much like the one we used in Section 9,
Falling Back, on page 91. We’ll include the jQuery library and the plugin,
we’ll detect whether the browser supports our attribute, and if it
doesn’t, we’ll activate the plug-in. In this case, we need to detect the
presence of the border-radius CSS property, but we also need to check
for browser-specific prefixes such as webkit and moz.
Create corner.js, and add this function:

function h a s B o r d e r R a d i u s O {
var element = document.documentElement;
var s t y l e = e l e m e n t . s t y l e ;
if ( s t y l e ) {
return t y p e o f s t y l e . b o r d e r R a d i u s == “string” ||
t y p e o f s t y l e . M o z B o r d e r R a d i u s == “string” ||
t y p e o f s t y l e . W e b k i t B o r d e r R a d i u s == “string” ||
t y p e o f s t y l e . K h t m l B o r d e r R a d i u s == “string”;
}
return null;

We can now detect whether our browser is missing support for rounded
corners, so let’s write the code to do the actual rounding. Thankfully,
there’s a plug-in that can get us started.

jQuery Corners

jQuery Corners2 is a small plug-in that rounds corners by wrapping
elements with additional div tags and styling them so that the targetelement looks rounded. It doesn’t work for form fields; however, with a
little imagination, we can use this plug-in and a little bit of j Query to
make it work.

First, grab jQuery Corners, and link to it from your HTML page. While
there, also link up your corner.js file.

<script src=”jquery.corner.js” charset=”utf-8″ type=’text/javascript’></script>
<script src=”corner.js” charset=”utf-8″ type=’text/javascript’></script>

 

Now we just have to write the code that actually invokes the rounding.

Our formCorners Plug-in

We’re going to write a jQuery plug-in so that we can easily apply this
rounding to all of the form fields. We already talked about writing
jQuery plug-ins in Section 5, Falling Back, on page 60, so I don’t need
to cover that again. Instead, I’ll just walk you through the code for this
plug-in, which is based in part on a solution by Tony Amoyal.

Add this to your corners.js file:

(function«) {
$ . f n . f o r m C o r n e r = function(){
return this.each(function() {
var i n p u t = $(this);
var input_background = input.css(“background-color”);
var i n p u t _ b o r d e r = input.cssC’border-color”);
i n p u t . e s s ( ” b o r d e r ” , “none”) ;
var wrap_width = p a r s e l n t ( i n p u t . c s s ( ” w i d t h ” ) ) + 4;
var wrapper = i nput .wrap( “<divx/div>”) . p a r e n t O ;
var border = wrapper .wrapC “<divx/div>”) . p a r e n t O ;
wrapper.ess C’background-col or”, i nput_background)
.essC’padding”, “lpx”) ;
b o r d e r . ess C’backg round-col or” , i n p u t _ b o r d e r )
.essC”width”, wrap_width + ” p x ” )
. c s s ( ‘padding’, ‘lpx’);
wrapper.cornerC”round 5px”);
b o r d e r . c o r n e r C”round 5px”) ;
} ) ;
} ;
}) CjQuery) ;

We’re taking a jQuery object that could be an element or a collection of
elements, and we’re wrapping it with two div tags that we then round.
We first make the innermost div the same color as the background of the
original input, and we turn off the border of the actual form field. Then
we wrap that field with another field with its own background color,
which is the color of the original input’s border color, and give it a little
bit of padding. This padding is what makes the border’s outline visible.
Imagine two pieces of construction paper—a green one that’s 4 inches
wide and the other a red one that’s 3 inches wide. When you place the
smaller one atop the larger one, you’ll see a green border around the
red one. That’s how this works.

Invoking the Rounding

With the plug-in and our detection library in place, we can now invoke
the rounding.

Add this to the corners.js file:

$(function O {
i f ( ! h a s B o r d e r R a d i u s ( ) ) {
$(“input”).formCornerO;
SC’fi eldset”) . corner Ç” round 5px”) ;
$(“legend”).corner(“round top 5px c c : # f f f ” );
} ) ;}

We’re rounding the three form fields and the fieldset, and finally, on
line 5, we’re rounding only the top part of the legend and specifying
that the cutout of the corner should use white. The plug-in uses the
background color of the parent for its cutaway color, and that’s not
appropriate here.

If the browser has support for the border-radius property, then it runs
our plug-in. If not, then it’ll use the CSS we added earlier.

A Minor Nudge

IE treats legends a little differently. We can add in a small style fix for
IE that pushes the fieldset’s legend up a bit so that it looks the same
as it does in Firefox and Chrome.

< l i n k r e l = ” s t y l e s h e e t ” h r e f = ” s t y l e . e s s ” t y p e = ” t e x t / c s s ” media=”screen”>
<! — [ i f I E ] >
< s t y l e >
f i e l d s e t 1 e g e n d { m a r g i n – t o p : -lOpx }
< / s t y l e >
< ! [ e n d i f ] – – >

Now things look relatively similar on all of the major browsers; you can
see the Internet Explorer version in Figure 8.2, on the preceding page.
Rounded corners add a bit of softness to your interfaces, and it is extremely
easy to use. That said, it’s important to be consistent with your
use and to not overuse this technique, just like any other aspect of
design.

 

 

Graphing Statistics with RGraph

AwesomeCo is doing a lot of work on the website, and senior management
would like to see a graph of the web stats. The back-end programmers
will be able to get the data in real time, but first they’d like
to see whether you can come up with a way to display the graph in the
browser, so they’ve provided you with some test data. Our goal is to
transform that test data into something that resembles Figure 6.2, on
the following page.

There are lots of ways to draw graphs on a web page. Developers use
Flash for graphs all the time, but that has the limitation of not working
on some mobile devices like the iPad or iPhone. There are server-side
solutions that work well, but those might be too processor-intensive if
you’re working with real-time data. A standards-based client-side solution
like the canvas is a great option as long as we’re careful to ensure it
works in older browsers. You’ve already seen how to draw squares, but
drawing something complex requires a lot more JavaScript. We need a
graphing library to help us along.

The fact that HTML5 isn’t available everywhere yet hasn’t stopped the
developers of the RGraph library.5 RGraph makes it ridiculously simple
to draw graphs using the HTML5 canvas. It’s a pure JavaScript solution,
though, so it won’t work for those user agents that don’t have
JavaScript available, but then again, neither will the canvas. Here’s the
code for a very simple bar graph:

<canvas width=”500″ height=”250″ id=”test”>[no canvas support]</canvas>
<script type=”text/javascript” charset=”utf-8″>
var bar = new RGraph.BarC’test’, [50,25,15,10]);
bar.SetC’chart.gutter’, 50);
bar.Set(‘chart.colors’, [‘red’]) ;
bar.SetC’chart.title’, “A bar graph of my favorite pies”);
bar.SetC’chart.labels’, [“Banana Creme”, “Pumpkin”, “Apple”, “Cherry”]);
bar.DrawC) ;
</script>

A client-side bar graphAll we have to do is create a couple of JavaScript arrays, and the library
draws the graph on the canvas for us.

Describing Data with HTML

We could hard-code the values for the browser statistics in the Java-
Script code, but then only users with JavaScript would be able to see
the values. Instead, let’s put the data right on the page as text. We can
read the data with JavaScript and feed it to the graphing library later.

<div id=”graph_data”>
<hl>Browser share for this site</hl>
<ul>
<li>
<p data-name=”Safari 4″ data-percent=”15″>
Safari 4 – 15%
</p>
</li>
<li>
<p data-name=”Internet Explorer” data-percent=”55″>
Internet Explorer – 55%
</p>
</li>
<li>
<p data-name=”Firefox” data-percent=”14″>
Firefox – 14%
</p>
</li>

<li>
<p data-name=”Google Chrome” data-percent=”16″>
Google Chrome – 16%
</p>
</li>
</ul>
</div>

We’re using the HTML5 data attributes to store the browser names and
the percentages. Although we have that information in the text, it’s
so much easier to work with programmatically since we won’t have to
parse strings.
If you open up the page in your browser or just look at Figure 6.3,
you’ll see that the graph data is nicely displayed and readable even
without the graph. This will be your fallback content for mobile devices
and other users where either the canvas element or JavaScript is not
available.
Now, let’s turn this markup into a graph.

Turning Our HTML into a Bar Graph

We’re going to use a bar graph, so we’ll need to require both the RGraph
Bar graph library as well as the main RGraph library. We’ll also use
j Query to grab the data out of the document. In the head section of the
HTML page, we need to load the libraries we need.

<script type=”text/javascript”
charset= “utf-8″
src=”http://ajax.googleapis.com/ajax/1ibs/jquery/1.4.2/jquery.min.js”>
</scri pt>
<script src=”javascri pts/RCraph. common, js” ></script>
<script src=”javascripts/RCraph.bar.js” ></script>

To build the graph, we need to grab the graph’s title, the labels, and
the data from the HTML document and pass it to the RGraph library.
RGraph takes in arrays for both the labels and the data. We can use
j Query to quickly build those arrays.

Une l function canvasGraph(){
var title = $(‘#graph_data hi’) .textO ;
var labels = $C”#graph_data>ul>li>p[data-name]”).map(function(){
5 return $(this),attr(“data-name”);
} ) ;
var percents = $C”#graph_data>ul>li>p[data-percent]”).map(function(){
return parselnt($(this).attr(“data-percent”)) ;
io });
var bar = new RGraph.Bar(‘browsers’, percents);
bar.SetC ‘chart.gutter’ , 50);
bar.Set(‘chart.colors’, [ ‘red’]) ;
15 bar. Set C ‘chart, title’ , title);
bar.Set( ‘chart.labels ‘ , labels);
bar.DrawO ;
}

First, on line 2, we grab the text for the header. Then, on line 4, we select
all the elements that have the data-name attribute. We use jQuery’s map
function to turn the values from those elements into an array.
We use that same logic again on line 8 to grab an array of the percentages.
With the data collected, RGraph has no trouble drawing our graph.

Displaying Alternative Content

In Section 14, Describing Data with HTML, on page 120, I could have
placed the graph between the starting and ending canvas tags. This would hide these elements on browsers that support the canvas while
displaying them to browsers that don’t. However, the content would
still be hidden if the user’s browser supports the canvas element but
the user has disabled JavaScript.

We simply leave the data outside the canvas and then hide it with
jQuery once we’ve checked that the canvas exists.

var canvas = document.getElementByld(‘browsers’);
if (canvas.getContext){
$C’#graph_data’) .hideO ;
canvasGraphO ;
}

Falling Back

When building this solution, we already covered fallbacks for accessibility
and lack of JavaScript, but we can create an alternative graph for
people who don’t have canvas support but can use JavaScript.
There are a ton of graphing libraries out there, but each one has its
own way of grabbing data. Bar graphs are just rectangles with specific
heights, and we have all the data on the page we need to construct this
graph by hand.

Line l function divGraph(barColor, textColor, width, spacer, lblHeight){
$(‘#graph_data ul’).hide();
var container = $C’#graph_data”) ;
5 container.css( {
“display” : “block”,
“position” : “relative”,
“height”: “300px”}
);
$ C”#graph_data>ul>li>p”).each(function(i){
var bar = $(“<div>” + $(this) .attrC’data-percent”) + “%</div>”) ;
var label = $(“<div>” + $(this) ,attr(“data-name”) + “</ch’v>”);
var commonCSS = {
“width”: width + “px”,
“position” : “absolute”,
“left” : i * (width + spacer) + “px”};
var barCSS = {
“background-color” : barColor,
“color” : textColor,
“bottom” : lblHeight + “px”,
25 “height” : $(this) .attr(“data-percent”) + “%”
};
var labelCSS = {“bottom” : “0”, “text-align” : “center”};
bar.cssC $.extend(barCSS, commonCSS) );
30 label.css( $. extend (label CSS, commonCSS) );
container.append(bar);
container.append(label);
});
}

On line 2, we hide the unordered list so that the text values are hidden.
We then grab the element containing the graph data and apply some
basic CSS styles. We set the positioning of the element to relative on 6,
which will let us absolutely position our bar graphs and labels within
this container.
Then we loop over the paragraphs in the bulleted list (line 11) and create
the bars. Each iteration over the labels creates two div elements, one for
the bar itself and another for the label, which we position below it. So,
with just a little bit of math and some j Query, we are able to re-create

Our graph now displays

the graph. Although it doesn’t look exactly the same, it’s close enough
to prove the concept.
We then just need to hook it into our canvas detection, like this:

if (canvas.getContext){
S(‘#graph_data’).h i de();
canvasGraphO;
}
else{
divGraph(“#f 00”, “#fff”, 140, 10, 20);
}

You can see the fallback version in Figure 6.4. With a combination
of JavaScript, HTML, and CSS, we’ve provided a client-side bar graph
and statistical information about browser usage to any platform that
requires it. Using the canvas has an additional benefit—it got us to
start thinking about a fallback solution from the beginning, rather than
trying to wedge something in later. That’s really good for accessibility.
This is one of the most accessible and versatile methods of graphing
data available. You can easily create the visual representation as well
as a text-based alternative. This way, everyone can use the important
data you’re sharing.

The Future

Now that you know a little about how the canvas works, you can start
thinking of other ways you might use it. You could use it to create
a game, create a user interface for a media player, or use it to build
a better image gallery. All you need to start painting is a little bit of
JavaScript and a little bit of imagination.

Right now, Flash has an advantage over the canvas because it has
wider support, but as HTML5 picks up and the canvas is available to a
wider audience, more developers will embrace it for simple 2D graphics
in the browser. The canvas doesn’t require any additional plug-ins
and uses less CPU than Flash, especially on Linux and OS X. Finally,
the canvas provides you with a mechanism to do 2D graphics in environments
where Flash isn’t available. As more platforms support the
canvas, you can expect the speed and features to improve, and you’ll
see more developer tools and libraries appear to help you build amazing
things.

But it doesn’t stop with 2D graphics. The canvas specification will eventually
support 3D graphics as well, and browser manufacturers are
implementing hardware acceleration. The canvas will make it possible
to create intriguing user interfaces and engaging games using only
JavaScript.

 

Drawing a Logo

The canvas element is a container element much like the script element. It’s a blank slate we can draw on. We define a canvas with a width and height like this:

<canvas id=”my_canvas” width=”150″ height=”150″>
Fallback content here
</canvas>

Unfortunately, you can’t use CSS to control or alter the width and height of a canvas element without distorting the contents, so you need to decide on your canvas dimensions when you  declare it.

We use JavaScript to put shapes on the canvas. Even if you provided fallback content to those browsers without the canvas element, you still need to prevent the JavaScript code from trying to manipulate it. Find the canvas by its ID, and see whether the browser supports the canvas’ getContext method.

var canvas = document.getElementById(‘my_canvas’);
if (canvas.getContext){
var context = canvas.getContext(‘2d’);
}else{
// do something to show the canvas’ hidden contents
// or let the browser display the text within the <canvas> element.
}

If we get a response from the getContext method, we grab the 2D context for the canvas so we can add objects. If we don’t have a context, we need to devise a way to display the fallback content. Since we know that the canvas element requires JavaScript in order to work, we’re building a framework to handle fallbacks from the beginning.

Once you have the canvas’ context, you simply add elements to that context. To add a red box, you set the fill color and then create the box, like this:

 

context.fi 11 Style = “rgb(200,0,0)” ;
context.fillRect (10, 10, 100, 100);

The canvas’s 2D context is a grid, with the top-left corner as the default origin. When you create a shape, you specify the starting X and Y coordinates and the width and height.

Each shape is added onto its own layer, so you could create three boxes with a 10-pixel offset, like this:

context, f i l l Style = “rgb(200,0,0)”;
context. fillRect (10, 10, 100, 100);
context.fill Style = “rgb(0,200,0)”;
context.fillRect (20, 20, 100, 100);
context.fill Style = “rgb(0,0,200)”;
context.fillRect (30, 30, 100, 100);

and they’ll stack on top of each other, like this:

Now that you have an understanding of the basics of drawing, let’s put together the AwesomeCo logo. It’s pretty simple, as you can see from Figure 6.1, on the following page.

Drawing the Logo

The logo consists of a string of text, an angled path, a square, and a triangle. Let’s start by creating a new HTML5 document, adding a canvas element to the page, and then creating a JavaScript function for drawing the logo, which detects whether we can use the 2D canvas.

var drawLogo = function(){
var canvas = document.getElementByld(‘logo’);
var context = canvas.getContext(‘2d’);
};

We invoke this method after first checking for the existence of the canvas
element, like this:

$(function(){
var canvas = document.getElementByld(‘logo’);
if (canvas.getContext){
drawLogo() ;
}

Notice here we’re using the j Query function again to ensure that the
event fires when the document is ready. We’re looking for an element
on the page with the ID of logo, so we’d better make sure we add our
canvas element to the document so it can be found, and our detection
will work.

 

<canvas id=”logo” width=”900″ height=”80″>
<hl>AwesomeCo</hl>
</canvas>

 

Next, let’s add the “AwesomeCo” text to the canvas.

Adding Text

Adding text to the canvas involves choosing a font, a font size, and an
alignment, and then applying the text to the appropriate coordinates
on the grid. We can add the text “AwesomeCo” to our canvas like this:

context.font = ‘italic 40px sans-serif’;
context.textBaseline = ‘top’;
context.fillTextC’AwesomeCo’, 60, 0);

We’re defining the text type and setting its baseline, or vertical alignment,
before we apply it to the canvas. We’re using the fillText method
so we get text that’s filled in with the fill color, and we’re setting it 60
pixels to the right so we can make room for the large triangle-shaped
path we’ll draw next.

Drawing Lines

We draw lines on the canvas by playing a game of “connect-the-dots.”
We specify a starting point on the canvas grid and then specify additional
points on the grid to move to. As we move around the canvas, the
dots get connected, like this:

Drawing LinesWe use the beginPathO method to start drawing a line, and then we
create our path, like this:

context.lineWidth = 2;
context.begi nPath Q;
context.moveTo(0, 40);
context.lineTo(30, 0);
context.lineTo(60, 40 );
context.lineTo(285, 40 );
context. strokeO ;
context.closePathQ ;

When we’re all done moving around the canvas, we have to call the
stroke method to draw the line and then call the closePath method to
stop drawing.
Now all that’s left is the box and triangle combination that sits within
the big triangle.

Moving the Origin

We need to draw a small square and triangle inside the larger triangle.
When we draw shapes and paths, we can specify the X and Y coordinates
from the origin at the top-left corner of the canvas, but we can
also just move the origin to a new location.
Let’s draw the smaller inner square by moving the origin.

context.save();
context.translate(20,20) ;
context.fillRect(0,0,20,20);

Notice that before we move the origin, we call the save() method. This
saves the previous state of the canvas so we can revert easily. It’s like
a restore point, and you can think of it as a stack. Every time you
call save(), you get a new entry. When we’re all done, we’ll call restoreO,
which will restore the top savepoint on the stack.

Now let’s use paths to draw the inner triangle, but instead of using a
stroke, we’ll use a fill to create the illusion that the triangle is “cutting
into” the square.

context.fi11 Style = ‘#fff’;
context.strokeStyle = ‘#fff’;
context.lineWidth = 2;
context.beginPathO ;
context.moveTo(0, 20);
context.lineTo(10, 0);
context.lineTo(20, 20 ) ;
context.lineTo(0, 20 );
context.fi11();
context.closePathO ;
context. restoreO ;

Here we set the stroke and fill to white (#fff) before we begin drawing.
Then we draw our lines, and since we moved the origin previously, we’re
relative to the top-left corner of the square we just drew.

We’re almost done, but it needs a little color.

Adding Colors

In Section 13, Moving the Origin, on the previous page, you saw briefly
how to set the stroke and fill color for the drawing tools. We could set
the color of everything to red just by adding this code before we draw
anything:

context.fi11 Style = “#fOO”;
context.strokeStyle = “#fOO”;

But that’s a little boring. We can create gradients and assign those to
strokes and fills like this:

// context.fi11 Style = “#fOO”;
// context.strokeStyle = “#fOO”;
var gradient = context.createLinearGradient(0, 0, 0, 40);
gradient.addColorStopCO, ‘#a00’); // red
gradient.addColorStop(l, ‘#f00’); // red
context.fi11 Style = gradient;
context.strokeStyle = gradient;

We just create a gradient object and set the color stops of the gradient.
In this example, we’re just going between two shades of red, but we
could do a rainbow if we wanted.2

Note that we have to set the color of things before we draw them.
At this point, our logo is complete, and we have a better understanding
of how we draw simple shapes on the canvas. However, versions of
Internet Explorer prior to IE9 don’t have any support for the canvas
element. Let’s fix that.

Falling Back

Google released a library called ExplorerCanvas3 that makes most of
the Canvas API available to Internet Explorer users. All we have to do
is include this library on our page:

<! — [ i f lte IE 8]>
<script src=”javascripts/excanvas.js”x/script>
<![endif]–>

and things should work just fine in Internet Explorer—but they don’t
work just yet. At the time of writing, the most stable release doesn’t support
adding text at all, and the version from the Subversion repository4
doesn’t use the correct fonts. Also, there’s no support yet for adding
gradients on strokes with this library.

So, instead, we rely on other solutions, such as placing a PNG of the
logo inside the canvas element, or we simply don’t use the canvas at all.
Since this was just an exercise to show you how to draw, it’s not the end
of the world if we can’t use this particular example in a cross-platform
production system yet.

 

Building Mobile Interfaces with Media Queries

We’ve been able to define media-specific style sheets for quite a while, but we’ve been limited to the type of output, as you saw In Making Links Printable with :ajter and content, on page 83, when we defined our print style sheet. CSS3’s media queries6 let us change the presentation
of a page based on the screen size our visitors use. Web developers have done screen size detection for years using JavaScript to obtain Information about the user’s screen size. But we can start to do that with style sheets alone. We can use media queries to determine the following:

• Resolution
• Orientation (portrait or landscape mode)
• Device width and height
• Width and height of the browser window

Because of this, media queries make It very easy for us to create alternative style sheets for mobile users.

It turns out that the AwesomeCo executive staff have all just dumped their BlackBerry devices for shiny new IPhones. The marketing director would love to see an IPhone-ready version of the blog template we built In Redefining a Blog Using Semantic Markup, on page 27. We can do that very quickly.

Our current blog Is a two-column layout, with a main content region and a sidebar. The easiest way to make this more readable on the IPhone Is to remove the floating elements so that the sidebar falls beneath the main content. That way, the reader won’t have to scroll sideways
on the device.

What About the Handheld Media Type?

The Handheld media type was designed to let us target mobile devices like we target printers, but most mobile devices want to show you the “real Internet” and so they ignore that media
type, serving the style sheet associated with the screen media type instead.

To make this work, we’ll add this code to the bottom of the blog’s style sheet:

@media only screen and (max-device-width: 480px) {
body{
width:460px;
section#sidebar, section#posts{
float: none;
width: 100%;

You can think of the code we put within the media query braces as its own style sheet, invoked when the conditions of the query are met. In this case, we resize the body of the page and turn our two-column layout into a single-column layout.

We could also use media queries when we link the style sheet, so we can keep our mobile style sheet in a separate file, like this:

<link rel=”stylesheet” type=”text/ess”
href=”CSS/mobi le. ess” media=”on7y screen and (max-device-width: 480px)”>

With that, our blog immediately becomes more readable on the iPhone. You can use this approach to build style sheets for other displays as well, such as kiosks, tablets, and displays of various sizes so that your content is readable in more places.

Falling Back

Media queries are supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 9. You’ll have to rely on JavaScript fallback solutions to load additional style sheets based on the user’s device. Our example targets iPhones, so we don’t need a fallback solution—our content is readable without the media query.

However, if you want to experiment with media queries in other browsers, there is a jQuery plug-in7 that adds basic media query support to other browsers. It’s limited in that it works only with linked style sheets, and it only supports min-width and max-width in pixels. Even with those limitations, it works very well for creating different interfaces for different window sizes.

The Future

The things we talked about in this chapter improve the user interface, but people can still work with our products if their browsers don’t support these new features. People can still read the data in the table if it’s not styled with stripes; the forms will still work, even if they don’t have
rounded corners on the interface elements; and the newsletter won’t be laid out in multiple columns. It’s good to know that we can use the presentation layer to achieve these effects instead of having to resort to JavaScript or server-side solutions.

Almost all browsers support these selectors now, with the exception of Internet Explorer. As we move forward, you can expect to see IE moving to support these as well, especially the pseudoclasses. When the specification becomes final, the vendor-specific prefixes like moz
and webkit- go away. Once that happens, you’ll be able to remove your fallback code.