Advanced Styling

In our quest to build an Android app without Java, we've discussed how to use CSS to style a collection of HTML pages to look like an Android app. In this chapter, we'll lay the groundwork to make those same pages behave like an Android app. Specifically, we'll discuss:

• Using Ajax to turn a full website into a single-page app.

• Creating a Back button with history using JavaScript.

• Saving the app as an icon on the home screen.

Adding a Touch of Ajax

The term Ajax (Asynchronous JavaScript and XML) has become such a buzzword that I'm not even sure I know what it means anymore. For the purposes of this book, I'm going to use the term Ajax to refer to the technique of using JavaScript to send requests to a web server without reloading the current page (e.g., to retrieve some HTML, submit a form). This approach makes for a very smooth user experience, but does require that you reinvent a lot of wheels.

For example, if you are loading external pages dynamically, the browser will not give any indication of progress or errors to the users. Furthermore, the Back button will not work as expected unless you take pains to support it. In other words, you have to do a lot of work to make a sweet Ajax app. That said, the extra effort can really pay off, because Ajax allows you to create a much richer user experience.

Traffic Cop

For the next series of examples, we'll write a single page called android.html that will sit in front of all the site's other pages. Here's how it works:

1. On first load, android.html will present the user with a nicely formatted version of the site navigation.

2. We'll then use jQuery to "hijack" the onclick actions of the nav links, so when the user clicks a link, the browser page will not navigate to the target link. Rather, jQuery will load a portion of the HTML from the remote page and deliver the data to the user by updating the current page.

We'll start with the most basic functional version of the code and improve it as we go along.

The HTML for the android.html wrapper page is extremely simple (see Example 3-1). In the head section, set the title and viewport options and include links to a stylesheet (android.css) and two JavaScript files: jquery.js and a custom JavaScript file named android.js.

ij. You must put a copy of jquery.js in the same directory as the HTML file.

For more information on where to get jquery.js and what to do with it, ' ^ I $ see "Introduction to JavaScript" on page 12. You should do this now before proceeding further.

The body has just two div containers: a header with the initial title in an h1 tag and an empty div container, which will end up holding HTML snippets retrieved from other pages.

Example 3-1. This simple HTML wrapper markup will sit in front of the rest of the site's pages

<title>Jonathan Stark</title>

<meta name="viewport" content="user-scalable=no, width=device-width" /> <link rel="stylesheet" href="android.css" type="text/css" media="screen" /> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="android.js"></script> </head> <body>

<div id="header"><h1>Jonathan Stark</h1></div> <div id="container"></div> </body> </html>

Let's move on to the android.css file. As you can see in Example 3-2, we're going to shuffle some of the properties from previous examples in Chapter 2 (e.g., some of the #header h1 properties have been moved up to #header), but overall everything should look familiar (if not, please review Chapter 2).

Example 3-2. The base CSS for the page is just a slightly shuffled version of previous examples body {

background-color: #ddd; color: #222; font-family: Helvetica; font-size: 14px;

background-color: #ccc;

background-image: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999)); border-color: #666; border-style: solid; border-width: 0 0 1px 0;

#header h1 { color: #222; font-size: 20px; font-weight: bold; margin: 0 auto; padding: 10px 0; text-align: center; text-shadow: 0px 1px 1px #fff;

list-style: none; margin: 10px; padding: 0;

background-color: #FFF; border: 1px solid #999; color: #222; display: block; font-size: 17px; font-weight: bold; margin-bottom: -1px; padding: 12px 10px; text-decoration: none;

ul li:first-child a {

-webkit-border-top-left-radius: 8px; -webkit-border-top-right-radius: 8px;

ul li:last-child a {

-webkit-border-bottom-left-radius: 8px; -webkit-border-bottom-right-radius: 8px;

ul li a:active,ul li a:hover { background-color:blue; color:white;

#content {

padding: 10px;

text-shadow: 0px 1px 1px #fff;

Setting Up Some Content to Work With

This JavaScript loads a document called index.html, and will not work without it. Before you proceed, copy the HTML file from Example 2-1 into the same directory as android.html, and be sure to name it index.html. However, none of the links in it will work unless the targets of the links actually exist. You can create these files yourself or download the example code from this book's website.

If you want a couple functioning links to play with, you can create about.html, blog.html, and consulting-clinic.html. To do so, just duplicate index.html a few times and change the filename of each copy to match the related link. For added effect, you can change the content of the h2 tag in each file to match the filename. For example, the h2 in blog.html would be <h2>Blog</h2>.

At this point, you should have the following files in your working directory:

android.html

You created this in Example 3-1. android.css

You created this in Example 3-2.

index.html

A copy of the HTML file in Example 2-1. about.html

A copy of index.html, with the h2 set to "About". blog.html

A copy of index.html, with the h2 set to "Blog". consulting-clinic.html

A copy of index.html, with the h2 set to "Consulting Clinic".

Routing Requests with JavaScript

The JavaScript in android.js is where all the magic happens in this example. Create this file in the same directory as your android.html file. Please refer to Example 3-3 as we go through it line by line.

Example 3-3. This bit of JavaScript in android.js converts the links on the page to Ajax requests

$('#container').load('index.html #header ul', hijackLinks); } else {

$('#container').load(url + ' #content', hijackLinks);

function hijackLinks() {

$('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href);

O Here we're using jQuery's document ready function to have the browser run the loadPage() function when the browser has finished constructing the page.

© The loadPage() function accepts a single parameter called url and then checks (on the next line) whether a value has been sent.

If a value is not sent into the function (as will be the case when it is called for the first time from the document ready function), url will be undefined and this line will execute. This line and the following are examples of jQuery's load() function. The load() function is excellent for adding quick and dirty Ajax functionality to a page. If this line were translated into English, it would read, "Get all of the ul elements from the #header element of index.html and insert them into the #container element of the current page. When you're done, run the hijackLinks() function."

O This line is executed if the url parameter has a value. It says, in effect, "Get the #content element from the url that was passed into the loadPage() function and insert it into the #container element of the current page. When you're done, run the hijackLinks() function."

© Once the load() function has completed, the #container element of the current page will contain the HTML snippet that was retrieved. Then, load() will run the hijackLinks() function.

© On this line, hijackLinks() finds all of the links in that new snippet of HTML and binds a click handler to them using the lines of code that follow. Click handlers are automatically passed an event object, which we're capturing as the function parameter e. The event object of a clicked link contains the URL of the remote page in e.target.href.

O Normally, a web browser will navigate to a new page when the user clicks a link. This navigation response is called the default behavior of the link. Since we are handling clicks and loading pages through JavaScript, we need to prevent this default behavior. On this line, which (along with the next line) is triggered when a user clicks one of the links, call the built-in preventDefault() method of the event object. If we leave that line out, the browser will dutifully leave the current page and navigate to the URL of clicked link.

> index.html refers to the home page of the site. If your home page is named differently, you'd use that filename here instead. If you've ' *y been following along, you used index.html.

O When the user clicks, pass the URL of the remote page to the loadPage() function, and the cycle starts all over again.

One of my favorite things about JavaScript is that you can pass a function as a parameter to another function. Although this looks weird at first, it's extremely powerful and allows you to make your code modular and reusable. If you'd like to learn more, you should check out Java Script: The Good Parts by Douglas Crockford (O'Reilly). In fact, if you are working with JavaScript, you should check out everything by Douglas Crockford; you'll be glad you did.

Click handlers do not run when the page first loads; they run when the user actually clicks a link. Assigning click handlers is like setting booby traps; you do some initial setup work for something that may or may not be triggered later.

* > It's worth taking a few minutes to read up on the properties of the event object that JavaScript creates in response to user actions in the browser. ' ' I' A good reference is located at http://www.w3schools.com/htmldom/dom _obj_event.asp.

When testing the code in this chapter, be sure you point your browser at the an-droid.html page. Web servers will typically default to displaying index.html if you just navigate to the directory that the files are in. Normally this is helpful, but in this case it will cause a problem.

Simple Bells and Whistles

With this tiny bit of HTML, CSS, and JavaScript, we have essentially turned an entire website into a single-page application. However, it still leaves quite a bit to be desired. Let's slick things up a bit.

Progress Indicator

Since we are not allowing the browser to navigate from page to page, the user will not see any indication of progress while data is loading (Figure 3-1). We need to provide some feedback to users to let them know that something is, in fact, happening. Without this feedback, users may wonder if they actually clicked the link or missed it, and will often start clicking all over the place in frustration. This can lead to increased server load and application instability (i.e., crashing).

Figure 3-1. Without a progress indicator of some kind, your app will seem unresponsive and your users will get frustrated

Thanks to jQuery, providing a progress indicator only takes two lines of code. We'll just append a loading div to the body when loadPage() starts and remove the loading div when hijackLinks() is done. Example 3-4 shows a modified version of Example 3-3. The lines you need to add to android.js are shown in bold.

Example 3-4. Adding a simple progress indicator to the page

$(document).ready(function(){ loadPage();

function loadPage(url) {

$('body').append('<div id="progress">Loading...</div>');

$('#container').load('index.html #header ul', hijackLinks); } else {

$('#container').load(url + ' #content', hijackLinks);

function hijackLinks() {

$('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href);

Simulating Real-World Network Performance

If you are testing this web application on a local network, the network speeds will be so fast you won't ever see the progress indicator. If you are using Mac OS X, you can slow all incoming web traffic by typing a couple of ipfw commands at the terminal. For example, these commands will slow all web traffic to 4 kilobytes per second:

sudo ipfw pipe 1 config bw 4KByte/s sudo ipfw add 100 pipe 1 tcp from any to me 80

You should use your computer's hostname or external IP address in the URL (for example, mycomputer.local rather than localhost). When you're done testing, delete the rule with sudo ipfw delete 100 (you can delete all custom rules with ipfw flush).

You can do similar things on Linux and Windows as well. For Linux, check out the following links:

http://linux-ip.net/articles/Traffic-Control-HOWTO/classless-qdiscs.html

http://lartc.org/howto/lartc.ratelimit.single.html

If you are using Windows, see the following:

http://blogs.msdn.com/b/wndp/archive/2006/06/30/653047.aspx

http://www.netlimiter.com

If you are using the Android emulator (see "Create an Android Virtual Device" on page 117), you can configure it to limit its speed using the -netspeed command-line option. For example, invoking the emulator with the arguments -netspeed edge will simulate real-world EDGE network speeds (118.4 kilobits per second upstream, 236.8 kilobits per second downstream). Run emulator -help-netspeed at the command line to see a list of all supported speeds.

See Example 3-5 for the CSS you need to add to android.css to style the progress div.

Example 3-5. CSS added to android.css used to style the progress indicator

#progress {

-webkit-border-radius: 10px;

color: white;

font-size: 18px;

font-weight: bold;

height: 80px;

left: 60px;

line-height: 80px;

margin: 0 auto;

position: absolute;

text-align: center;

top: 120px;

width: 200px;

Setting the Page Title

Our site happens to have a single h2 at the beginning of each page that would make a nice page title (see Figure 3-2). You can see this in the HTML source shown in Chapter 2. To be more mobile-friendly, we'll pull that title out of the content and put it in the header (see Figure 3-3). Again, jQuery to the rescue: you can just add three lines to the hijackLinks() function to make it happen. Example 3-6 shows the hijackLinks function with these changes.

Example 3-6. Using the h2 from the target page as the toolbar title function hijackLinks() {

$('#container a').click(function(e){ e.preventDefault(); loadPage(e.target.href);

ffiiOH 750 lHD<a 3:24pm

10 http://jonathanstark.co... U

Jonathan Stark

About

Jonathan Stark is a web developer, speaker, and author. His consulting firm, Jonathan Stark Consulting, Inc., has attracted clients such as Nokia, Turner Broadcasting, and the PGA Tour.

Jonathan is the author of the book Building Android Apps with HTML, CSS, and JavaScript, is a regular speaker at leading industry conferences, and is often quoted in the media on internet and mobile lifestyle trends

Jonathan began his programming career more than 20 years ago on a Tandy TRS-80 and still thinks Zork was a sweet game.

In his own words

I have forever been interested in two things: computers and music. So, after spending the better part of my teens and twenties attempting to cultivate a career in music, I

Figure 3-2. Before moving the page heading to the toolbar...

0 http://jonathanstark.co...

Jonathan Stark is a web developer, speaker, and author. His consulting firm, Jonathan Stark Consulting, Inc., has attracted clients such as Nokia, Turner Broadcasting, and the PGA Tour.

Jonathan is the author of the book Building Android Apps with HTML, CSS, and JavaScript, is a regular speaker at leading industry conferences, and is often quoted in the media on internet and mobile lifestyle trends

Jonathan began his programming career more than 20 years ago on a Tandy TRS-80 and stili thinks Zork was a sweet game.

In his own words

I have forever been interested in two things: computers and music. So, after spending the better part of my teens and twenties attempting to cultivate a career in music, I finally crossed over to the dark side and took a desk job as a graphic designer.

Figure 3-3. ...and after moving the page heading to the toolbar

I added the title lines before the line that removes the progress indicator. I like to remove the progress indicator as the very last action because I think it makes the application feel more responsive.

The double pipe (||) in the first line of inserted code (shown in bold) is the JavaScript logical operator OR. Translated into English, that line reads, "Set the title variable to the HTML contents of the h2 element, or to the string 'Hello!' if there is no h2 element." This is important because the first page load won't contain an h2 because we are just grabbing the nav uls.

* > This point probably needs some clarification. When users first load the android.html URL, they are only going to see the overall site navigation ' ' 1,4' elements, as opposed to any site content. They won't see any site content until they tap a link on this initial navigation page.

Handling Long Titles

Suppose we had a page on our site with a title too long to fit in the header bar (Figure 3-4). We could just let the text break onto more than one line, but that would not be very attractive. Instead, we can update the #header h1 styles such that long text will be truncated with a trailing ellipsis (see Figure 3-5 and Example 3-7). This might be my favorite little-known CSS trick.

Example 3-7. Adding an ellipsis to text that is too long for its container

#header h1 { color: #222; font-size: 20px; font-weight: bold; margin: 0 auto; padding: 10px 0; text-align: center; text-shadow: 0px 1px 1px #fff; max-width: 160px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;

IEO075« liffle 3:34pm

H http://jonathanstark.co... ^

Consulting Clinic

Jonathan Stark Is a web developer, speaker, and author. His consulting firm, Jonathan Stark Consulting, Inc., has attracted clients such as Nokia, Turner Broadcasting, and the PGA Tour.

Jonathan is the author of the book Building Android Apps with HTML, CSS, and lavaScript. is a regular speaker at leading Industry conferences, and is often quoted in the media on internet and mobile lifestyle trends

Jonathan began his programming career more than 20 years ago on a Tandy TRS-SO and stlli thinks Zork was a sweet game.

In his own words

I have forever been interested In two things: computers and music. So, after spending the better part of my teens and twenties attempting to cultivate a career in music, I finally crossed over to the dark side and took a desk inh as a pranhir ri^sipnpr

Figure 3-4. Text wrapping in the toolbar is not very attractive...

O http://jonathanstark.co... ^

Consulting Cli

Jonathan Stark is a web developer, speaker, and author. His consulting firm, Jonathan Stark Consulting, Inc., has attracted clients such as Nokia, Turner Broadcasting, and the PGA Tour.

Jonathan is the author of the book Building Android Apps with HTML, CSS, and JavaScript, is a regular speaker at leading industry conferences, and is often quoted in the media on internet and mobile lifestyle trends

Jonathan began his programming career more than 20 years ago on a Tandy TRS-80 and still thinks Zork was a sweet game.

In his own words

I have forever been interested in two things: computers and music. So, after spending the better part of my teens and twenties attempting to cultivate a career in music, I finally crossed over to the dark side and took a desk job as a graphic designer.

Figure 3-5. ...but we can beautify it with a CSS ellipsis

Here's the rundown: max-width: 160px instructs the browser not to allow the h1 element to grow wider than 160px. Then, overflow: hidden instructs the browser to chop off any content that extends outside the element borders. Next, white-space: nowrap prevents the browser from breaking the line into two. Without this line, the h1 would just get taller to accommodate the text at the defined width. Finally, text-overflow: ellip sis appends three dots to the end of any chopped-off text to indicate to the user that she is not seeing the entire string.

Automatic Scroll-to-Top

Let's say you have a page that is longer than the viewable area on the phone. The user visits the page, scrolls down to the bottom, and clicks on a link to an even longer page. In this case, the new page will show up "prescrolled" instead of at the top as you'd expect.

Technically, this makes sense because we are not actually leaving the current (scrolled) page, but it's certainly a confusing situation for the user. To rectify the situation, we can add a scrollTo() command to the loadPage() function (Example 3-8).

Whenever a user clicks a link, the page will first jump to the top. This has the added benefit of ensuring the loading graphic is visible if the user clicks a link at the bottom of a long page.

Example 3-8. It's a good idea to scroll back to the top when a user navigates to a new page function loadPage(url) {

$('body').append('<div id="progress">Loading...</div>');

scrollTo(0,0);

$('#container').load('index.html #header ul', hijackLinks); } else {

$('#container').load(url + ' #content', hijackLinks);

Beginners Guide To Graphics Design

Beginners Guide To Graphics Design

Spilling The Secret Of How To Get Yourself Recognized As A Top Rate Graphics Designer And Win Work From Major Companies For Your Graphic Design Services. Are you a graphic designer struggling to find clients?

Get My Free Ebook


Post a comment