Finding Yourself: Geolocation

Geolocation is a technique for discovering where people are, based on their computer’s location. Of course, “computer” really can mean smart phone, tablet, or other portable device as well as a desktop computer. Geolocation determines a person’s whereabouts by looking at their computer’s IP address, MAC address, Wi-Fi hotspot location, or even GPS coordinates if available. Although not strictly part of HTML5 the specification, Geolocation is often associated with HTML5 because it’s coming on the scene at the same time. Unlike Web Storage, Geolocation was never part of the HTML5 specification. Like Web Storage, it’s a very useful technology that is already implemented in Firefox, Safari, and Chrome. Let’s see how we can use it.

Locating Awesomeness

We’ve been asked to create a contact page for the AwesomeCo website, and the CIO has asked whether we could show people’s location on a map along with the various AwesomeCo support centers. He’d love to see a prototype, so we’ll get one up and running quickly.

We’ll use Google’s Static Map API for this because it doesn’t require an API key and we’re just going to generate a very simple map.

AwesomeCo service centers are located in Portland, Oregon; Chicago, Illinois; and Providence, Rhode Island. Google’s Static Map API makes it really easy to plot these points on a map. All we have to do is construct an img tag and pass the addresses in the URL, like this:

<img id=”map” alt= ” M a p of AwesomeCo Service Center locations”
s r c = ” h t t p : / / m a p s . g o o g l e . c o m / m a p s / a p i / s t a t i cmap?
&amp;si ze=900×300
& a m p ; s e n s o r = f a l s e
&amp;maptype=roadmap
& a m p ; m a r k e r s = c o l o r : g r e e n | l a b e l : A | 1 + D a v o l + s q u a r e , + P r o v i d e n c e , + R I + 0 2 9 0 6 – 3 8 1 0
& a m p ; m a r k e r s = c o l o r : g r e e n | l a b e l : B | 2 2 + S o u t h w e s t + 3 r d + A v e n u e , P o r t l a n d , + O R
& a m p ; m a r k e r s = c o l o r : g r e e n | l a b e l : C | 7 7 + W e s t + W a c k e r + D r i v e + C h i c a g o + I L ” >

We define the size of the image, and then we tell the Maps API that we did not use any sensor device, such as a GPS or client-side geolocation with the information we’re passing to this map. Then we define each marker on the map by giving it a label and the address. We could use a comma-separated pair of coordinates for these markers if we had them, but this is easier for our demonstration.

How to Be Found

We need to plot our visitor’s current location on this map, and we’ll do that by providing another marker on the map by using our latitude and longitude for a new marker. We can ask the browser to grab our visitor’s latitude and longitude, like this:

n a v i g a t o r . g e o l o c a t i o n . g e t C u r r e n t P o s i t i o n ( f u n c t i o n ( p o s i t i o n ) {
showLocati o n ( p o s i t i o n . c o o r d s . 1 a t i t u d e , posi t i o n . c o o r d s . 1 ongi t u d e ) ;
} ) ;

This method prompts the user to provide us with their coordinates. If the visitor allows us to use their location information, we call the showLocation( ) method.

The showLocation() method takes the latitude and longitude and reconstructs the image, replacing the existing image source with the new one. Here’s how we implement that method:

Une l var showLocati on = f u n c t i o n ( l a t , l n g ) {
2 var fragment = “&markers=color:redlcolor:redllabel :YI” + l a t + “, ” + l n g ;
3 var image = $ ( ” # m a p ” ) ;
4 var source = i m a g e . a t t r ( ” s r c ” ) + f r a g m e n t ;
5 source = source . r e p l a c e ( “sensor=false” , “sensor=true”) ;
6 i m a g e . a t t r ( ” s r c ” , s o u r c e );
7 };

Rather than duplicate the entire image source code, we’ll append our location’s latitude and longitude to the existing image’s source.

Before we assign the modified image source back to the document, we need to change the sensor parameter from false to true. We’ll do that on line 5 with the replace() method.

When we bring it up in our browser, we’ll see our location, marked with a “Y” among the other locations. To see an example, take a look at Figure 10.3, on the next page.

Falling Back

As it stands, visitors to the page will still see the map with the locations of the AwesomeCo support centers, but we will get a JavaScript error if we try to load our page. We need to detect support for geolocation before we attempt to get the visitor’s location, like this:

i f ( n a v i g a t o r . g e o l o c a t i o n ) {
n a v i g a t o r . g e o l o c a t i o n . g e t C u r r e n t P o s i t i o n ( f u n c t i o n ( p o s i t i o n ) {
showLocati o n ( p o s i t i o n . c o o r d s . 1 a t i t u d e , posi t i o n . c o o r d s . 1 o n g i t u d e ) ;
} ) ;
} e l s e {

Google’s Ajax API11 does location lookup, so it’s a great fallback solution. You will need to obtain an API key to use this on your site when you go live, but you don’t need one to try this locally.

Our fallback looks like this:

Une i var key = “your_key” ;
var s c r i p t = “http://www.google.com/jsapi?key=” + key;
$ . g e t S c r i p t ( s c r i p t , f u n c t i o n O {
i f (Ctypeof google == ‘ o b j e c t ‘ ) &&
5 google.loader && g o o g l e . l o a d e r . C l i e n t L o c a t i o n ) {
showLocati on(google.1oade r . C I i e n t L o c a t i o n . 1 a t i t u d e ,
g o o g l e . 1 o a d e r . C I i e n t L o c a t i on.1ongi t u d e ) ;
} e l s e {
var message = $(“<p>Couldn’t find your address.</p>”);
io message . i n s e r t A f t e r C “#map”) ;
};
} ) ;

We’re using jQuery’s getScript() method to load the Google Ajax API. We then use Google’s ClientLocation() method on line 5 to get a visitor’s location and invoke our showLocation() method to plot the location on our map.

Unfortunately, Google can’t geolocate every IP address out there, so we may still not be able to plot the user on our map; therefore, we account for that by placing a message underneath our image on line 9. Our fallback solution isn’t foolproof, but it does give us a greater chance of locating our visitor.

Without a reliable method of getting coordinates from the client, we’ll just need to provide a way for the user to provide us with an address, but that’s an exercise I’ll leave up to you.

The Future

The techniques we talked about in this chapter, although not all part of HTML5 proper, represent the future of web development. We’ll be pushing many more things to the client side. Better history management will make Ajax and client-side applications much more intuitive. Web Sockets can replace periodic polling of remote services for the display of
real-time data. Cross-document Messaging lets us merge web applications that usually would never be able to interact, and Geolocation will eventually let us build better location-aware web applications, which become more and more relevant every day with the growing mobile computing market.

Explore these APIs and keep an eye on their adoption. You may soon find these to be invaluable tools in your web development toolbox.

 

Web Sockets

Real-time interaction has been something web developers have been trying to do for many years, but most of the implementations have involved using JavaScript to periodically hit the remote server to check for changes. HTTP is a stateless protocol, so a web browser makes a connection to a server, gets a response, and disconnects. Doing any kind of real-time work over a stateless protocol can be quite rough. The HTML5 specification introduced Web Sockets, which let the browser make a stateful connection to a remote server.7 We can use Web Sockets to build all kinds of great applications. One of the best ways to get a feel for how Web Sockets work is to write a chat client, which, coincidentally, AwesomeCo wants for its support site.

AwesomeCo wants to create a simple web-based chat interface on its support site that will let members of the support staff communicate internally, because the support staff is located in different cities. We’ll use Web Sockets to implement the web interface for the chat server.
Users can connect and send a message to the server. Every connected user will see the message. Our visitors can assign themselves a nickname by sending a message such as “/nick brian,” mimicking the IRC chat protocol. We won’t be writing the actual server for this, because that has thankfully already been written by another developer.

The Chat Interface

We’re looking to build a very simple chat interface that looks like Figure 10.2, on the next page, with a form to change the user’s nickname, a large area where the messages will appear, and, finally, a form to post a message to the chat.

In a new HTML5 page, we’ll add the markup for the chat interface, which consists of two forms and a div that will contain the chat messages.

<div id=”chat_wrapper”>
<h2>AwesomeCo Help!</h2>
<form id=”nick_form” action=”#” method=”post” accept-charset=”utf-8″>
<p>
<label>Ni ckname
<input id=”nickname” type=”text” value=”Guestl)ser”/>
</label>
<input type=”submit” value=”Change”>
</p>
</form>
<div id=”chat”>connecting….</div>
<form id=”chat_form” action=”#” method=”post” accept-charset=”utf-8″>
<p>
<label>Message
<input id=”message” type=”text” />
</label>
<input type=”submit” value=”Send”>
</p>
</form>
< / d i v >

We’ll also need to add links to a style sheet and a JavaScript file that will contain our code to communicate with our Web Sockets server.

<script src=’chat.js’ type=’text/javascript’></script>
<link rel=”stylesheet” href=”style.css” media=”screen”>

Our style sheet contains these style definitions:

Une l #chat_wrapper{
w i d t h : 320px;
h e i g h t : 440px;
b a c k g r o u n d – c o l o r : #ddd;
5 padding: lOpx;
}
#chat_wrapper h2{
m a r g i n : 0;
}
# c h a t {
w i d t h : 300px;
h e i g h t : 300px;
o v e r f l o w : auto;
15 background-color: #fff;
p a d d i n g : lOpx;
}

On line 14, we set the overflow property on the chat message area so that its height is fixed and any text that doesn’t fit should be hidden, viewable with scrollbars.

With our interface in place, we can get to work on the JavaScript that will make it talk with our chat server.

Talking to the Server

No matter what Web Sockets server we’re working with, we’ll use the same pattern over and over. We’ll make a connection to the server, and then we’ll listen for events from the server and respond appropriately.

EventIn our chat.js file, we first need to connect to our Web Sockets server, like this:

v a r webSocket = new W e b S o c k e t ( ‘ w s : / / l o c a l h o s t : 9 3 9 4 / ‘ ) ;

When we connect to the server, we should let the user know. We define the onopen() method like this:

webSocket.onopen = f u n c t i o n ( e v e n t ) {
$ ( ‘ # c h a t ‘ ) . a p p e n d ( ‘ < b r > C o n n e c t e d to the s e r v e r ‘ ) ;
};

When the browser opens the connection to the server, we put a message in the chat window. Next, we need to display the messages sent to the chat server. We do that by defining the onmessage() method like this:

webSocket.onmessage = f u n c t i o n ( e v e n t ) {
$ ( ‘ # c h a t ‘ ) . a p p e n d ( ” < b r > ” + e v e n t . d a t a ) ;
$ ( ‘ # c h a t ‘ ) . a n i m a t e ( { s c r o l l T o p : $ ( ‘ # c h a t ‘ ) . h e i g h t O } ) ;

The message comes back to us via the event object’s data property. We just add it to our chat window. We’ll prepend a break so each response falls on its own line, but you could mark this up any way you wanted.

Next we’ll handle disconnections. The onclose() method fires whenever the connection is closed.

w e b S o c k e t . o n c l o s e = f u n c t i o n ( e v e n t ) {
$ ( ” # c h a t ” ) . a p p e n d ( ‘ < b r > C o n n e c t i o n c l o s e d ‘ ) ;
};
};

Now we just need to hook up the text area for the chat form so we can send our messages to the chat server.

$ ( f u n c t i o n ( ) {
$( “form#chat_form”) . submi t ( f u n c t i o n ( e ) {
e . p r e v e n t D e f a u l t ( ) ;
v a r t e x t f i e l d = $(“#message”) ;
w e b S o c k e t . s e n d ( t e x t f i e l d . v a l ( ) ) ;
t e x t f i e l d . v a l ( ” ” ) ;
} ) ;

We hook into the form submit event, grab the value of the form field, and send it to the chat server using the send() method.

We implement the nickname-changing feature the same way, except we prefix the message we’re sending with “/nick.” The chat server will see that and change the user’s name.

$( “form#nick_form”) . submi t ( f u n c t i o n ( e ) {
e . p r e v e n t D e f a u l t ( ) ;
var t e x t f i e l d = $(“#m’c/cname”) ;
webSocket.send(“/m’c/c ” + t e x t f i e l d . v a l ( ) ) ;
} ) ;

That’s all there is to it. Safari 5 and Chrome 5 users can immediately participate in real-time chats using this client. Of course, we still need to support browsers without native Web  Sockets support. We’ll do that using Flash.

Falling Back

Browsers may not all have support for making socket connections, but Adobe Flash has had it for quite some time. We can use Flash to act as our socket communication layer, and thanks to the web-socket-js9 library, implementing a Flash fallback is a piece of cake.

We can download a copy of the plug-in10 and place it within our project. We then need to include the three JavaScript files on our page:

<script type=”text/javascript” src=”websocket_js/swfobject.js”x/script>
<script type=”text/javascript” src=”websocket_js/FABridge.js”x/script>
<script type=”text/javascript” src=”websocket_js/web_socket.js”x/script>
<script src=’chat.js’ type=’text/javascript’></script>
<link rel=”stylesheet” href=”style.css” media=”screen”>
</head>
<body>
<div id=”chat_wrapper”>
<h2>AwesomeCo Help!</h2>
<form id=”nick_form” action=”#” method=”post” accept-charset=”utf-8″>
<p>
<label>Ni ckname
<input id=”nickname” type=”text” value=”Guestl)ser”/>
</label>

<input type=”submit” value=”Change”>
</p>
</form>
<div id=”chat”>connecting….</div>
<form id=”chat_form” action=”#” method=”post” accept-charset=”utf-8″>
<p>
<label>Message
<input id=”message” type=”text” />
</label>
<input type=”submit” value=”Send”>
</p>
</form>
< / d i v >
</body>
</html>

The only change we need to make to our chat.js file Is to set a variable that specifies the location of the WebSocketMain file.

WEB_SOCKET_SWF_|_OCATION “websocket_js/WebSocketMain. swf” ;

With that In place, our chat application will work on all major browsers, provided that the server hosting your chat server also serves a Flash Socket Policy file.

Flash Socket Policy What?

For security purposes, Flash Player will only communicate via sockets with servers that allow connections to Flash Player. Flash Player attempts to retrieve a Flash Socket Policy file first on port 843 and then on the same port your server uses. It will expect the server to return a
response like this:

<cross-domain-policy>
<allow-access-from domain=”*” to-ports=”*” />
</cross-domain-policy>

This Is a very generic policy file that allows everyone to connect to this service. You’d want to specify the policy to be more restrictive if you were working with more sensitive data. Just remember that you have to serve this file from the same server that’s serving your Web Sockets server, on either the same port or the port 843.

The example code for this section contains a simple Flash Socket Policy server written in Ruby that you can use for testing. See Section 25,

Servers, for more on how to set that up on your own environment for testing.

Chat servers are just the beginning. With Web Sockets, we now have a robust and simple way to push data to our visitors’ browsers.

Servers

The book’s source code distribution contains a version of the Web Sockets server we’re targeting. It’s written in Ruby, so you’ll need a Ruby interpreter. For instructions on getting Ruby working on your system, see the file RUBY_README.txt within the book’s source code files.

You can start it up by navigating to its containing folder and typing this:

ruby s e r v e r . r b

In addition to the chat server, there are two other servers you may want to use while testing the examples in this chapter. The first server, client.rb, serves the chat interface and JavaScript files. The other server, flashpolicyserver, serves a Flash Policy file that our Flash-based Web Sockets fallback code will need to contact in order to connect to the
actual chat server. Flash Player uses these policy files to determine whether it is allowed to talk to a remote domain.

If you’re running on a Mac or a Linux-based operating system, you can start all these servers at once with this:

rake s t a r t

from the html5_websockets folder.

 

 

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!

 

 

Preserving History

The HTML5 specification introduces an API to manage the browser history. 2 In Creating an Accessible Updatable Region, on page 104, we built a prototype for AwesomeCo’s new home page that switched out the main content when we clicked one of the navigation tabs. One drawback with the approach we used is that there’s no support for the browser’s Back button. We can fix that with some hacks, but we will eventually be able to solve it for good with the History API.

We can detect support for this API like this:

function s u p p o r t s H i s t o r y O {
return ! ! ( w i n d o w . h i s t o r y && w i n d o w . h i s t o r y . p u s h S t a t e ) ;}

We use this method whenever we need to work with the History objects.

Storing the Current State

When a visitor brings up a new web page, the browser adds that page to its history. When a user brings up a new tab, we need to add the new tab to the history ourselves, like this:

Une l $( “nav ul “) . cl i c k(function(event) {
t a r g e t = $ ( e v e n t . t a r g e t ) ;
i f ( t a r g e t . i s ( ” a ” ) ) {
e v e n t . p r e v e n t D e f a u l t ( ) ;
5 if ( $ ( t a r g e t . a t t r ( ” h r e f ” ) ) .hasCl ass ( ” h i d d e n ” ) ){
i f ( s u p p o r t s H i s t o r y ( ) ) {
I- var tab = $ ( t a r g e t ) . a t t r ( ” h r e f ” ) ;
I- var s t a t e O b j e c t = { t a b : t a b } ;
! • window.history.pushState(stateObject, t a b ) ;
};
$( ” .visible”) . removed ass ( ” v i si b 7 e ” ) . addCl as s( “hidden”) . h i d e ( ) ;
$ ( t a r g e t . a t t r ( “href”)) . removed ass (“hidden”) . addClass( “visible”) . show() ;
is };
};
} ) ;
} ) ;

We snag the ID of the element that’s visible, and then we add a history state to the browser. The first parameter of the pushstateO method is an object that we’ll be able to interact with later. We’ll use this to store the ID of the tab we want to display when our user navigates back to this point. For example, when the user clicks the Services tab, we’ll store #services in the state object.

The second parameter is a title that we can use to identify the state in our history. It has nothing to do with the title element of the page; it’s just a way to identify the entry in the browser’s history. We’ll use the ID of the tab again.

Retrieving the Previous State

Although this adds a history state, we still have to write the code to handle the history state change. When the user clicks the Back button, the window.onpopstateO event gets fired. We use this hook to display the tab we stored in the state object.

i f ( s u p p o r t s H i s t o r y O ) {
window.onpopstate = function(event) {
i f ( e v e n t . s t a t e ) {
var tab = ( e v e n t . s t a t e [ ” t a b ” ] ) ;
$(“. visible”)
. removed ass{“visible”)
.addClass(“hidden”)
. h i d e O ;
$ ( t a b )
. r e m o v e d a s s ( ” h i d d e n ” )
. a d d C l a s s ( ” v i s i b 7 e ” )
. show() ;
}
};

We fetch the name of the tab and then use jQuery to locate the element to hide by its ID. The code that hides and shows the tabs is repeated here from the original code. We should refactor this to remove the duplication.

Defaulting

When we first bring up our page, our history state is going to be null, so we’ll need to set it ourselves. We can do that right above where we defined our window.onpopstateO method.

i f ( s u p p o r t s H i s t o r y O ) {
w i n d o w . h i s t o r y . p u s h S t a t e C { t a b : “#we7come”}, ‘^welcome’);
window.onpopstate = function(event) {
i f ( e v e n t . s t a t e ) {
var tab = ( e v e n t . s t a t e [ ” t a b ” ] ) ;
$(“. visible”)
. removed ass(“visible”)
.addClass(“hidden”)
. h i d e O ;
$ ( t a b )
. r e m o v e d a s s ( ” h i d d e n ” )
. a d d d a s s ( ” v i s i b 7 e ” )
. show() ;
}
};
};

Now, when we bring up the page, we can cycle through our tabs using the browser history.

Falling Back

This works in Firefox 4 and Safari 4, as well as in Chrome 5, but it doesn’t work in Internet Explorer. Solutions like the jQuery Address plug-in4 provide the same functionality, but we won’t go into implementing that as a fallback solution because it’s less of a fallback and
more of a complete replacement with a lot of additional features. Keep an eye on browser support for history manipulation, though, because you’ll be able to easily provide much more user-friendly applications when you can use this API in every browser.

 

Storing Data in a Client-Side Relational Database

The localStorage and sessionStorage methods give us an easy way to store simple name/value pairs on the client’s computer, but sometimes we need more than that. The HTML5 specification initially introduced the ability to store data in relational databases. It’s since been spun off into a separate specification called Web SQL Storage.6 If you have even
a basic background in writing SQL statements, you’ll feel right at home in no time. To get you comfortable, we’ll use Web SQL Storage to create, retrieve, update, and destroy notes in a client-side database.

CRUD in Your Browser

The term CRUD, an acronym for “Create, Retrieve, Update, and Delete,” 7 pretty much describes what we can do with our client-side database. The specification and  implementations allow us to insert, select, update, and delete records.

AwesomeCo wants to equip their sales team with a simple application to collect notes while they’re on the road. This application will need to let users create new notes, as well as update and delete existing ones. To change existing notes, we’ll need to let users retrieve them from the database.

Here are the SQL statements we’ll need to write in order to make this happen:

SQL statementsThe Notes Interface

The Interface for the notes application consists of a left sidebar that will have a list of the notes already taken and a form on the right side with a title field and a larger text area for the note itself. Look at Figure 9.2, on the following page to see what we’re building.

To start, we need to code up the interface.

<!doctype html>
<html>
<head>
<title>AwesomeNotes</title>
<1 ink rel=”stylesheet” href=”style.css”>
< s c r i p t type=”text/javascript”
charset= “utf-8”
src=
“http://aj ax.googleapis.com/ajax/1ibs/jquery/1.4.2/jquery.min.js”>
</script>
< s c r i p t type=”text/javascript”
charset=”utf-8″ src=”javascripts/notes.js”>
</script>
</head>

<body>
<section id=”sidebar”>
<input type=”button” id=”new_button” value=”New note”>
<ul id=”notes”>
</ul>
</section>
<section id=”main”>
<form>
<ol>
<li>
<input type=”submit” id=”save_button” value=”Save”>
<input type=”submit” id=”delete_button” value=”Delete”>
</li>
<li>
<1abel for=”title”>Title</label>
<input type=”text” id=”title”>
</li>
<li>
<label for=”note”>Note</label>
<textarea id=”note”x/textarea>
</li>
</ol>
</form>
</section>
</body>
</html>

We define the sidebar and main regions using section tags, and we have given IDs to each of the important user interface controls like the Save button. This will make it easier for us to locate elements so that we can attach event listeners.

We’ll also need a style sheet so that we can make this look more like the figure, style.ess looks like this:

# s i d e b a r , #main{
d i s p l a y : b l o c k ;
f l o a t : l e f t ;
# s i d e b a r {
w i d t h : 2 5%;
#mai n{
w i d t h : 75%;
f o rm o l {
l i s t – s t y l e : none;
m a r g i n : 0;
p a d d i n g : 0;
}
f o rm 1 i {
p a d d i n g : 0;
m a r g i n : 0;
}
f o rm l i l a b e l {
d i s p l a y : b l o c k ;
}
# t i t l e , #note{
w i d t h : 100%;
f o n t – s i z e : 20px;
b o r d e r : lpx s o l i d #000;
# t i t l e{
h e i g h t : 20px;
}
# n o t e {
h e i g h t : 40px;
}

This style sheet turns off the bullet points, sizes the text areas, and lays things out in two columns. Now that we have the interface done, we can build the JavaScript we need to make this work.

Connecting to the Database

We need to make a connection and create a database:

/ / Database reference
var db = null ;
/ / Creates a connection to the l o c a l database
connectToDB = functionO
{
db = window.openDatabaseC’awesome_notes’, ‘1.0’ ,
‘AwesomeNotes Database’, 1024*1024*3);

We’re declaring the db variable at the top of our script. Doing this makes it available to the rest of the methods we’ll create.8 We then declare the method to connect to the database by using the window.openDatabase  method. This takes the name of the database, a version number, a description, and a size parameter.

Creating the Notes Table

Our notes table needs three columns:

c r e a t e N o t e s T a b l e = functionO
{
d b . t r a n s a c t ! on(function( t x ) {
t x . e x e c u t e S q l (
“CREATE TABLE notes ( i d INTEGER
PRIMARY KEY, t i t l e TEXT, note TEXT)”, [] ,
function(){ a l e r t ( ‘ N o t e s database created successfully!’); },
function( t x , e r r o r ) { a l e r t ( e r r o r . m e s s a g e ) ; } );
} ) ;

We fire the SQL statement inside a transaction, and the transaction has two callback methods: one for a successful execution and one for a failure. This is the pattern we’ll use for each of our actions.

Note that the executeSqK) method also takes an array as its second parameter. This array is for binding placeholders in the SQL to variables. This lets us avoid string concatenation and is similar to prepared statements in other languages. In this case, the array is empty because
we have no placeholders in our query to populate.

Now that we have our first table, we can make this application actually do something.

Loading Notes

When the application loads, we want to connect to the database, create the table if it doesn’t already exist, and then fetch any existing notes from the database.

/ / loads a l l records from the notes t a b l e of the database;
f e t c h N o t e s = function(){
d b . t r a n s a c t i o n ( f u n c t i o n ( t x ) {
t x . e x e c u t e S q l (‘SELECT id, title, note FROM notes ‘ , [] ,
f u n c t i o n C S Q L T r a n s a c t i o n , d a t a ) {
for (var i = 0 ; i < d a t a . r o w s . 1 e n g t h ; ++i) {
var row = d a t a . r o w s . i t e r n ( i ) ;
var i d = row[ ‘id’] ;
var t i t l e = row[ ‘ ti tie ‘ ] ;
addToNotesLi s t ( i d , t i t l e ) ;
}
} ) ;
} ) ;

This method grabs the results from the database. If it’s successful, it loops over the results and calls the addNoteToList method that we define to look like this:

/ / Adds the l i s t i t em t o the l i s t of notes, given an id and a t i t l e .
addToNotesLi s t = function( i d , t i t l e ) {
var notes = $(“#notes”);
var i t em = $ ( ” < l i > ” ) ;
i t e m . a t t r C ” d a t a – i d ” , i d ) ;
i t e m . h t m l ( t i t l e ) ;
n o t e s . a p p e n d ( i t e m ) ;
};

We’re embedding the ID of the record Into a custom data attribute. We’ll use that ID to locate the record to load when the user clicks the list Item. We then add the new list Item we create to the unordered list In our Interface with the ID of notes. Now we need to add code to load that Item Into the form when we select a note from this list.

Fetching a Specific Record

We could add a click event to each list Item, but a more practical approach Is to watch any clicks on the unordered list and then determine which one was clicked. This way, when we add new entries to the list (like when we add a new note), we don’t have to add the click event to the list.

Within our jQuery function, we’ll add this code:

$(“#notes”).cli ck(function(event){
if (SCevent.target),is( ‘li ‘ ) ) {
var element = SCevent.target);
loadNoteCelement.attr(“data-id”)) ;
}

This fires off the loadNoteO method, which looks like this:

loadNote = function(id){
db.transaction(function(tx) {
tx.executeSql (‘SELECT id, title, note FROM notes where id = ?’, [id],
functionCSQLTransaction, data){
var row = data.rows.itern(0);
var title = $(“#tit7e”) ;
var note = $(“#note”);
title.val (row[ “ti t7e”]) ;
ti tl e .attrC “data-id” , row[ “id”]) ;
note.val(row[“note”]);
$C “#delete_button”) . showO ;
} ) ;
} ) ;

This method looks a lot like the previous fetchNotesO method. It fires a SQL statement, and we then handle the success path. This time, the statement contains a question-mark  placeholder, and the actual value is in the second parameter as a member of the array.

When we have found a record, we display it in the form. This method also activates the Delete button and embeds the ID of the record into a custom data attribute so that updates can easily be handled. Our Save button will check for the existence of the ID. If one exists, we’ll update the record. If one is missing, we’ll assume it’s a new record. Let’s write that bit of logic next.

Inserting, Updating, and Deleting Records

When a user clicks the Save button, we want to trigger code to either insert a new record or update the existing one. We’ll add a click event handler to the Save button by placing this code inside the j Query function:

$(“#save_button”).cli ck(function(event){
event.preventDefault();
var title = $(“#tit7e”) ;
var note = $(“#note”);
i f ( t i tle.attr(“data-id”)){
updateNote(title, note);
}el se{
insertNoteCtitle, note);
}

This method checks the data-id attribute of the form’s title field. If it has no ID, the form assumes we’re inserting a new record and invokes the insertNote method, which looks like this:

i{n sertNote = functionCtitle, note)
db.transact!on(function(tx){
tx.executeSql (“INSERT INTO notes (title, note) VALUES (?, ?)”,
[title.val () , note .val ()] ,
function(tx, result){
var id = result.insertld ;
al ert( ‘Record ‘ + id+ ‘ saved!’);
title.attr(“data-id”, result.insertld );
addToNotesLi st(id , title.val ()) ;
$ C”#delete_button”).show();
} ,

functionO {
a l e r t C ‘ T h e note could not be saved.’);
}
);
} ) ;

The insertNoteO method inserts the record into the database and uses the insertld property of the resultset to get the ID that was just inserted. We then apply this to the “title” form field as a custom data attribute and invoke the addToNotesListO method to add the note to our list on the side of the page.

Next, we need to handle updates. The updateNoteO method looks just like the rest of the methods we’ve added so far:

updateNote = function( t i t l e , note)
{
var id = t i t l e . a t t r C ” d a t a – i d ” ) ;
d b . t r a n s a c t i o n(function( t x ) {
t x . e x e c u t e S q l (“UPDATE notes set title = ?, note = ? where id = ?”,
[ t i t l e . v a l () , note . val 0 , i d ] ,
function( t x , r e s u l t ) {
a l e r t ( ‘Record ‘ + id + ‘ updated!’);
$C”#notes>li[data-id=” + id + “] ” ) . h t m l ( t i t l e .val ()) ;
},
functionO {
a l e r t C ‘ T h e note was not updated!’);
}
);
} ) ;

When the update statement is successful, we update the title of the note in our list of notes by finding the element with the data-id field with the value of the ID we just updated.

As for deleting records, it’s almost the same. We need a handler for the delete event like this:

$ C”#delete_button”).cli ckCfunction(event){
e v e n t . p r e v e n t D e f a u l t ( ) ;
var t i t l e = $ ( ” # t i t 7 e ” ) ;
d e l e t e N o t e ( t i t l e ) ;
} ) ;

Then we need the delete method itself, which not only removes the record from the database but also removes it from the list of notes in the sidebar.

d e l e t e N o t e = f u n c t i o n ( t i t l e )
{
var id = t i t l e . a t t r ( ” d a t a – i d ” ) ;
d b . t r a n s a c t i o n ( f u n c t i o n ( t x ) {
t x . e x e c u t e S q l (“DELETE from notes where id = ?”, [ i d ] ,
function( t x , r e s u l t ) {
a l e r t ( ‘ R e c o r d ‘ + id + ‘ deleted!’);
$(“#notes>li[data-id=” + id + “] “) . remove() ;
} ,
functionO {
a l e r t ( ‘ T h e note was not deleted!’) ;
}
);
} ) ;

Now we just need to clear out the form so we can create a new record without accidentally duplicating an existing one.

Wrapping Up

Our notes application is mostly complete. We just have to activate the New button, which clears the form out when clicked so a user can create a new note after they’ve edited an  existing one. We’ll use the same pattern as before—we’ll start with the event handler inside the j Query function for the New button:

$(“#new_button”).cli ck(function( e v e n t ){
e v e n t . p r e v e n t D e f a u l t ( ) ;
newNoteO ;
} ) ;
/ / e n d : n e w b u t t o n
newNoteO ;

Next we’ll clear out the data-id attribute of the “title” field and remove the values from the forms. We’ll also hide the Delete button from the interface.

newNote = function(){
$(“#delete_button”) . h i d e O ;
var t i t l e = $ ( ” # t i ‘ t 7 e ” ) ;
t i t l e . r e m o v e A t t r C”data-id”);
t i t l e . v a l (“”) ;
var note = $ ( ” # n o t e ” ) ;
n o t e . val(“”) ;
}

We should call this newForm method from within our jQuery function when the page loads so that the form Is ready to be used. This way, the Delete button Is hidden too.

That’s all there Is to It. Our application works on IPhones, Android devices, and desktop machines running Chrome, Safari, and Opera. However, there’s little chance this will ever work In Flrefox, and It’s not supported In Internet Explorer either.

Falling Back

Unlike our other solutions, there are no good libraries available that would let us Implement SQL storage ourselves, so we have no way to provide support to Internet Explorer users natively. However, if this type of application is something you think could be useful, you could
convince your users to use Google Chrome, which works on all platforms, for this specific application. That’s not an unheard of practice, especially if using an alternative browser allows you to build an internal application that could be made to work on mobile devices as well.

Another alternative is to use the Google Chrome Frame plug-in.9 Add this to the top of your HTML page right below the head tag:

<meta http-equiv=”X-UA-Compatible” content=”chrome=l”>

This snippet gets read by the Google Chrome Frame plug-in and activates it for this page.

If you want to detect the presence of the plug-in and prompt your users to install it if it doesn’t exist, you can add this snippet right above the closing body tag:

< s c r i p t type=”text/javascript”
src=
“http://aj ax.googleapis.com/aj ax/1i bs/ch rome-frame/l/CFInstall.min.js”>
</script>
<script>
window.attachEventC”on7oad”, f u n c t i o n O {
C F I n s t a l 1 . c h e c k ( {
mode: “inline”, // the d e f a u l t
node: “prompt”
});
});
</script>

This will give the user an option to install the plug-in so they can work with your site.

Google Chrome Frame may not be a viable solution for a web application meant to be used by the general public, but it works well for internal applications like the one we just wrote. There may be corporate IT policies that prohibit something like this, but I’ll leave that up to you
to work out how you can get something like this approved if you’re in that situation. Installing a plug-in is certainly more cost-effective than writing your own SQL database system.

With HTML5’s Offline support,10 we can use HTML and related technologies to build applications that can still function while disconnected from the Internet. This is especially useful for developing applications for mobile devices that may drop connections.

This technique works in Firefox, Chrome, and Safari, as well as on the iOS and Android 2.0 devices, but there’s no fallback solution that will work to provide offline support for Internet Explorer.

AwesomeCo just bought its sales team some iPads, and they’d like to make the notes application we developed in Storing Data in a Client-Side Relational Database, on page 181, work offline. Thanks to the HTML5 manifest file, that will be a simple task.

Defining a Cache with the Manifest

The manifest file contains a list of all the web application’s client-side files that need to exist in the client browser’s cache in order to work offline. Every file that the application will reference needs to be listed here in order for things to work properly. The only exception to this is that the file that includes the manifest doesn’t need to be listed; it is cached implicitly.

Create a file called notes.manifest. Its contents should look like this:

CACHE MANIFEST
# v = 1 . 0 . 0
/ s t y l e . e s s
/ j a v a s c r i p t s / n o t e s . j s
/ j a v a s c r i p t s / j q u e r y . m i n . j s

The version comment in this file gives us something we can change so that browsers will know that they should fetch new versions of our files. When we change our code, we need to modify the manifest.

Also, we’ve been letting Google host jQuery for us, but that won’t work if we want our application to work offline, so we need to download jQuery and modify our script tag to load jQuery from our javascripts folder.

< s c r i p t t y p e = ” t e x t /javascript”
c h a r s e t = “utf-8″
src=”javascripts/jquery.min.js”>
< / s c r i p t >

Next, we need to link the manifest file to our HTML document. We do this by changing the html element to this:

<html manifest=”notes.manifest”>

That’s all we need to do. There’s just one little catch—the manifest file has to be served by a web server, because the manifest must be served using the text/cache-manifest MIME type. If you’re using Apache, you can set the MIME type in an .htaccess like this:

AddType t e x t / c a c h e – m a n i f e s t .manifest

After we request our notes application the first time, the files listed in the manifest get downloaded and cached. We can then disconnect from the network and use this application offline as many times as we want.

Be sure to investigate the specification. The manifest file has more complex options you can use. For example, you can specify that certain things should not be cached and should never be accessed offline, which is useful for ignoring certain dynamic files.

Manifest and Caching

When you’re working with your application in development mode, you are going to want to disable any caching on your web server. By default, many web servers cache files by setting headers that tell browsers not to fetch a new copy of a file for a given time. This can trip you up while you’re adding things to your manifest file.

If you use Apache, you can disable caching by adding this to your .htaccess file.

E x p i r e s A c t i v e On
E x p i r e s D e f a u l t “access”

This disables caching on the entire directory, so It’s not something you want to do In production. But this will ensure that your browser will always request a new version of your manifest file.

If you change a file listed In your manifest, you’ll want to modify the manifest file too, by changing the version number comment we added

The Future

Features like localStorage and Web SQL Databases give developers the ability to build applications In the browser that don’t have to be connected to a web server. Applications like the ones we worked on run on an IPad or Android device as well, and when we combine them with the HTML5 manifest file, we can build offline rich applications using familiar tools Instead of proprietary platforms. As more browsers enable support, developers will be able to leverage them more, creating applications that run on multiple platforms and devices, that store data locally, and that could sync up when connected.

The future of Web SQL Storage Is unknown. Mozllla has no plans to Implement It In Flrefox, and the W3C Is choosing Instead to move forward Implementing the IndexedDB specification. We’ll talk more about that specification In Section 11.5, Indexed Database API, on page 229.
However, Web SQL Storage has been In use on the IOS and Android devices for a while, and It’s likely to stay. This specification could be extremely useful to you if you’re developing applications in that space.

 

 

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.

 

 

Embedding Video

AwesomeCo wants to showcase its new series of training videos on its
website, and it wants the videos to be viewable on as many devices
as possible, especially on the iPad. As a trial, we’ve been provided two
videos in the “Photoshop Tips” series that we’ll use to build a prototype.
Thankfully, we’ve been given the video files in H.264, Theora, and VP8
format, so we can focus on creating the page.4

The video tag works exactly like the audio element. We just need to
provide our sources, and Chrome, Firefox, Safari, the iPhone, the iPad,
and Internet Explorer 9 will display the video without any additional
plug-ins. The markup for our first video file, 01_blur, looks like this:

<article>
<header>
<h2>Saturate w i t h Blur</h2>
</header>
<video controls»
<source src=”video/h264/01_blur.mp4″>
<source src=”video/theora/01_blur.ogv”>
<source src=”video/webm/01_blur.webm”>
<p>Your browser does not support the video tag.</p>
</video>
</article>

We’re defining the video tag with controls. We’re implicitly telling it that
it should not play automatically by not including the autoplay attribute.
At this point, our videos play in a wide variety of browsers, and our
users will see a video player similar to the one shown in Figure 7.2, on
the next page.

We still can’t reach users of Internet Explorer 8 and older. We’ll need to
use Flash to make that work.

Safari's HTML5Falling Back

To properly support a Flash-based fallback and still use HTML5 video,
we place the Flash object code within the video tag. The site Video For
Everybody5 outlines this process in great detail, but we’ll go over a basic
implementation here.
Flowplayer6 is a Flash-based player that can play our already-encoded
H.264 video. We’ll download the open source version of the player, and
we’ll place the flowplayer-x.x.x.swf and flowplayer-controls-x.x.x.swf files in
our project’s swf folder to keep things organized.
We then place this code inside our video tag, right after our last source
element:

<object width=”640″ height=”480″ type=”application/x-shockwave-flash”
data=”swf/flowplayer-3.2.2 ,swf”>
<param name=”movie” value=”swf/flowplayer-3.2.2.swf” />
<param name=”allowfullscreen” value=”true” />
<param name=”flashvars”val ue=’ c o n f i g = { “clip” : { “url ” : “. ./video/h264/01_blur.mp4” ,
“autoPlay”:false,
“autoBuffering”: true
}
} ‘ />
<img src= “video/thumbs/01_blur.prig”
width=”640″ height=”264″ alt=”Poster Image”
t i t l e = ” N o video playback capabilities.” />
</object>
Pay close attention to this part:
Down! oad html5video/index.html
<param name=”flashvars”
val ue=’ c o n f i g = { “clip” : { “url ” : “. ./video/h264/01_blur.mp4” ,
“autoPlay”:false,
“autoBuffering”: true
}
} ‘ />

The video file’s source needs to be relative to the location of Flowplayer.
Since we placed Flowplayer in the swf folder, we need to use the path
,./video/h264/01_blur.mp4 to get the player to see our video.
When we bring up our page in Internet Explorer, our video plays, and
we don’t need to encode to another format, thanks to Flowplayer. Our
Internet Explorer friends will see Figure 7.3, on the following page.
Of course, we still have to come up with a way for people who don’t
have native video support and don’t have Flash installed. To make that
happen, we’ll let people download our video content by adding another
section with download links.

<section>
<header>
<h3>Downloads</h3>
</header>
<ul>
< l i x a href=”video/h264/01_b1ur.mp4″>H264, playable on most platforms</ax/l i>
< l i x a href=”video/theora/01_b1ur.ogv’VOGG format</a></1 i>
< l i x a href=”video/webm/01_b1ur.webm’VWebM format</ax/1 i>
</u”l>
</section>

Our video in Internet ExplorerWe could use JavaScript to hide these videos if HTML5 video isn’t supported,
like this:

function c a n P l a y V i d e oO {
return ! ! d o c u m e n t . c r e a t e E l e m e n t C ‘ v i d e o ‘ ) . c a n P l a y T y p e;
}
i f ( c a n P l a y V i d e o ( ) ) {
$ ( # v i d e o s . d o w n l o a d s ) . h i d e ( ) ;
}

This uses a detection technique very similar to the one we used in Working
with Audio, on page 133. In our case, it makes more sense to let
people download these videos for use on their iPods or iPads so they
can watch them later.

Limitations of HTML5 Video

There are three very important limitations that currently limit the usefulness
of HTML5 video.
First, HTML5 video has no provisions for streaming the video files.
Users have become accustomed to being able to seek to a specific part
of a video. This is something that Flash-based video players excel at,
because of the amount of effort Adobe has put into Flash as a video

Media Content JavaScript API

In this chapter, we just briefly touched on the JavaScript APIs for
the audio and video elements. The full API can detect the types
of audio files the browser can play, and it provides methods to
control the playback of the audio elements.
In Working with Audio, on page 133, we built a page with multiple
sound samples. We could use the JavaScript API to make
all the sounds play at (roughly) the same time. Here’s a really
simplified approach:

var element = $ ( ” < p x i n p u t type=’button’ value=’Play all’/></p>”)
e l e m e n t . c l i ck(function(){
$ ( ” a u d i o ” ) .each(function(){
t h i s . p l a y O ;
} )
SC’body”) .append(element) ;

We’re creating a “Play all” button that, when pressed, cycles
through all the audio elements on the page and calls the piayO
method on each element.
We can do similar things with videos. There are methods to start
and pause elements and even auery the current time.
Unfortunately, at the time of writing, the JavaScript API isn’t well
supported everywhere. That shouldn’t discourage you from
looking at the possibilities outlined in the specification* to see
what’s possible.

delivery platform. To seek with HTML5 video, the file must be downloaded
completely on browsers. This may change in time.
Second, there’s no way to manage rights. Sites such as Hulu7 that
want to prevent piracy of their content can’t rely on HTML5 video. Flash
remains a viable solution for these situations.

Finally, and most importantly, the process of encoding videos is costly
and time-consuming. The need to encode in multiple formats makes
HTML5 video much less attractive. For that reason, you see many sites
supplying video in the patent-encumbered H.264 format so that it can
be played on the iPod and iPad, using a combination of the HTML5
video tag and Flash.
These issues aren’t going to derail HTML5, but they are things to be
aware of before we can use HTML5 video to replace Flash as a video
delivery vehicle.

Audio, Video, and Accessibility

None of the fallback solutions works really well for users with disabilities.
In fact, the HTML5 specification explicitly points that out. A hearing Impaired user won’t find any value in being able to download the
audio file, and a visually impaired user won’t have much use for a video
file they can view outside of the browser. When we provide content to
our users, we should provide usable alternatives whenever possible.
Video and audio files should have transcripts that people can view. If
you produce your own content, transcripts are easy to make if you plan
them from the start because they can come right from the script you
write. If a transcript isn’t possible, consider a summary that highlights
the important parts of the video.

<section>
<h2>Transcri pt</h2>
<p>We’ll drag the e x i s t i n g l a y e r to the new b u t t o n on the bottom of
t h e Layers p a l e t t e to c r e a t e a new copy.</p>
<p>Next w e ‘ l l go to the F i l t e r menu and choose “Gaussian Blur”.
W e ‘ l l change the b l u r amount j u s t enough so t h a t we lose a l i t t l e
b i t of the d e t a i l of the image.</p>
<p>Now w e ‘ l l d o u b l e – c l i c k on the l a y e r to e d i t the l a y e r and
change the b l e n d i n g mode to “Overlay”. We can then a d j u s t the
amount of the e f f e c t by changing the o p a c i t y s l i d e r . < / p >
<p>Now we have a s l i g h t l y enhanced image.</p>
</section>

You can hide the transcript or link to it from the main video page. As
long as you make it easy to find and easy to follow, it’s going to be really
helpful.

The Future

First-class audio support in the browser opens up a ton of new possibilities
for developers. JavaScript web applications can easily trigger
sound effects and alerts without having to use Flash to embed the
audio. Native video makes it possible to make video available to devices
such as iPhones, but it also gives us an open, standard method of interacting
with videos using JavaScript. Most importantly, we’ll be able to
treat video and audio clips just like we treat images, by marking them
up semantically and making them easier to identify.