Chapter 3. Advanced iPhone Styling

In our quest to build an iPhone app without Objective-C, we’ve so far learned how to use CSS to style a collection of HTML pages to look like an iPhone app. In this chapter, we’ll lay the groundwork to make those same pages behave like an iPhone app. Specifically, we’ll discuss using Ajax to turn a full website into a single page app, how to create a back button with history using JavaScript, and how to take advantage of the Web Clip Icon and Full Screen Mode features of the iPhone to launch your app without Mobile Safari intruding upon the user experience.

2 comments

  1. Nile Posted 1 month, 14 days and 22 hours ago

    This is very unclear - the scattered blocks of code, and no file names specified. You need to explicitly state the file names and the code they contain. For example, you mention iphone.html and index.php, but don't explain what code goes inside them. jquery.js and iphone.js are mentioned, but it is not explained what code goes inside them. Be clear and specific, and better yet, provide the final code in a functional version for us to test out and observe.

  2. Jonathan Stark Posted 1 month, 14 days and 21 hours ago

    Great comments. I totally agree and will fix. Thanks, and please keep the comments coming.

Add a comment

Adding a Touch of Ajax

The term Ajax 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 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, etc.). This approach makes for a very smooth user experience, but does require that you reinvent a lot of wheels.

4 comments

  1. Nile Posted 1 month, 15 days and 2 hours ago

    HMTL > HTML

    "to retrieve some HMTL, submit a form"

  2. Andrew Posted 1 month, 6 days and 18 hours ago

    "...much of the browser wheel" is an awkward phrase. I know what you're getting at but browsers obviously don't have wheels. Maybe "...but does require you to build by hand some of what a web browser handles for you when building regular websites." Something like that?

  3. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Thanks! Typo fixed, wheels left as is ;-)

  4. Leslie Murphy Posted 13 days and 1 hour ago

    The term Ajax has become such a buzzword that I’m not even sure I know what it means anymore.

    Wouldn't it be better for the reader to state something positive? e.g. "The term Ajax is widely used, and is believed to have been first coined by Jesse James Garett in 2005". See http://www.adaptivepath.com/ideas/essays/archives/000385.php for his original essay.

Add a comment

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. Even so, there are some very good reasons to go to the trouble. In particular, it opens the door to creating iPhone apps that can run full-screen (the section called “Full Screen Mode”) and even offline (Chapter 6, Going Offline).

3 comments

  1. Geoff R Hobbs Posted 1 month and 22 days ago

    very good reasons to go to the trouble

  2. Andrew Posted 1 month, 6 days and 18 hours ago

    You should refer to "the user" (singlular) as you've done consistently to this point.

  3. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Andrew and @Geoff - fixed, thanks!

Add a comment

Traffic Cop

For my next series of examples, I’m going to write a single page called iphone.html that will sit in front of all of the site’s other pages. Here’s how it works: On first load, iphone.html will present the user with a nicely formatted version of the site navigation. I’ll then use jQuery to “hijack” the onclick actions of the nav links so that when the user clicks on one, 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. I’ll start with the most basic functional version of the code and improve it as we go along.

4 comments

  1. Geoff R Hobbs Posted 1 month and 22 days ago

    he/she will be redirected to iphone.html

  2. Ben Parizek Posted 1 month, 6 days and 18 hours ago

    You might want to include an example of the code that redirects a user from another page to this page.

  3. Ben Parizek Posted 1 month, 6 days and 18 hours ago

    Ok, I will suggest that you definitely include the code for how to redirect. I'm stuck and spinning my wheels and I'd really like to be learning more instead...

  4. Stefan Ladstätter Posted 26 days and 14 hours ago

    Seconded.

Add a comment

The HTML for the iphone.html wrapper page is extremely simple (see Example 3.1, “This simple HTML wrapper markup will sit in front of the rest of the site's pages.”). In the head section, I set the title and viewport options, and include links to a stylesheet (iphone.css) and two JavaScript files: jquery.js and a custom JavaScript file named iphone.js.

Note

For more information on where to get jquery.js and what to do with it, see the section called “Intro to JavaScript”.

3 comments

  1. Joe Jones Posted 1 month, 6 days and 18 hours ago

    This is the third or fourth reminder about jQuery. I think that is two or three too many. Maybe a requirements section to the book that states what you are expecting the user to need as far as toolkits would do the trick in one location (preferably at the beginning of the book).

  2. Andrew Posted 1 month, 6 days and 18 hours ago

    Agreed. By this point it should be clear that it's necessary for the examples in the book. Besides, if the reader's done the earlier examples, they will have already gotten jquery.

  3. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    I hear ya. Imma leave this one to the editors. They tend to like to sprinkle cross references around for readers who prefer skipping around in the book. Not everyone is goes cover-to-cover...

Add a comment

The body just has 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.

                
<html>
<head>
    <title>Jonathan Stark</title>
    <meta name="viewport" content="user-scalable=no, width=device-width" />
    <link rel="stylesheet" href="iphone.css" type="text/css" media="screen" />
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="iphone.js"></script>
</head>
<body>
    <div id="header"><h1>Jonathan Stark</h1></div>
    <div id="container"></div>
</body>
</html>
                
            

2 comments

  1. William Doane Posted 1 month, 5 days and 20 hours ago

    in chapter 2, you assumed that jquery.js and iphone.js were located in the scripts/ folder. Also, that iphone.js was in the styles/ folder.

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Thanks! I updated ch2 to match this chapter.

Add a comment


Moving on to the iphone.css CSS file, you can see in Example 3.2, “The base CSS for the page is just a slightly reshuffled version of previous examples. ” that I’ve reshuffled some of the properties from previous examples (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, Basic iPhone Styling).

Example 3.2. The base CSS for the page is just a slightly reshuffled version of previous examples.

body {
    background-color: #ddd;
    color: #222;
    font-family: Helvetica;
    font-size: 14px;
    margin: 0;
    padding: 0;
}
#header {
    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 0px #fff;
}
ul {
    list-style: none;
    margin: 10px;
    padding: 0;
}
ul li a {
    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 0px #fff;
}
#content a {
    color: blue;
}
            

2 comments

  1. Mike Boulet Posted 1 month, 22 days and 23 hours ago

    kinda obvious, but I think it is worth mentioning that this is the iphone.css file

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Yup, agreed. I'll update it.

Add a comment


The JavaScript in iphone.js is where all the magic happens in this example. Please refer to Example 3.3, “This bit of JavaScript in iphone.js converts the links on the page to Ajax requests.” as I go through it line by line.

Note

This JavaScript loads a document called index.html, and will not work without it. You should reuse the HTML file from Chapter 2, Basic iPhone Styling, just be sure to save it as index.html in the same directory as the iphone.html you created earlier in this chapter. 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 the book's web site (see the section called “How to Contact Us”). Creating about.html, blog.html, consulting-clinic.html will give you a few links to play with. 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>.

3 comments

  1. Bhavesh Mehta Posted 27 days and 18 hours ago

    Don't see any code on "How to Contact Us" page.

  2. Mr. Nobody Posted 24 days and 21 hours ago

    I was confused by the index.html and iphone.html filenames. I'm using the very simple server of python -m SimpleHTTPServer, so when I ran the example, the server sent index.html, and things got weird. I only figured it out when I manually pulled up iphone.html.

    It would probably be better if you switched index.html and iphone.html, then the correct one would be served up. Or, change index.html to something else.

    Otherwise, keep it up. Great stuff.

  3. Leslie Murphy Posted 13 days and 1 hour ago

    Will you be posting chapter by chapter examples on an oreilly site? It would be great if you could refer readers to a set of zip files with the evolving state of the app as you continued to evolve it.

Add a comment

Example 3.3. This bit of JavaScript in iphone.js converts the links on the page to Ajax requests.

$(document).ready(function(){ 1
    loadPage();
});
function loadPage(url) {2
   if (url == undefined) {
      $('#container').load('index.html #header ul', hijackLinks);3
   } else {
      $('#container').load(url + ' #content', hijackLinks);4
   }
}
function hijackLinks() {5
    $('#container a').click(function(e){6
        e.preventDefault();7
          loadPage(e.target.href);8
    });
}
            

6 comments

  1. Mike Boulet Posted 1 month, 22 days and 23 hours ago

    mention this is iphone.js

  2. Jakob Torp Svendsen Posted 1 month, 15 days and 12 hours ago

    Instead of on each load, applying the click handlers on each click, you should instead use the .live() function to bind all a tags. Doing this, you wont need your callback function at all, since live will work on anything that gets generated as well.

    Something like this should do the trick. $('#container a').live("click", function(e){ e.preventDefault() loadPage(e.target.href); });

  3. Jonathan Stark Posted 1 month, 14 days and 21 hours ago

    Thanks for the comment. jQuery's live() function doesn't work with click on the iPhone. I should have added a note about this for folks like yourself who are familiar with jQuery. Thanks, and please keep the comments coming.

  4. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Mike - I've updated with a reference to the filename.

    @Jakob - I added a note about the live() function in chapter 5.

  5. Ric Kolseth Posted 10 days and 21 hours ago

    I'm a little confused on how to handle the iphone.js file: does this replace the original, or simply append to it?

  6. Ric Kolseth Posted 10 days and 20 hours ago

    I did figure this out: it might be good to make clear that the css and the .js files on this chapter must be kept separate from the css and the .js files previous

Add a comment


1

Here I’m using jQuery’s document ready function to have the browser run the loadPage() function when the DOM is complete.

2

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

1 comment

  1. Leslie Murphy Posted 13 days and 1 hour ago

    Suggest The loadPage() function accepts a optional parameter called url. It first checks (on the next line) whether a value has been sent.

Add a comment

3

If a value is not sent into the 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 in 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.” Note that index.html refers to the home page of the site. If your home page is named differently, you’d use that filename here instead.

5 comments

  1. William Doane Posted 1 month, 5 days and 20 hours ago

    "Note that index.php refers to the home page of the site. If your home page is named differently, you’d use that filename here instead."

    Wouldn't it be preferable to specify in chapter 2 what the primary file should be called and to build on/leverage that here?

  2. William Doane Posted 1 month, 5 days and 20 hours ago

    Given that I've only read this far, so I don't know where you're taking this example, but...

    In a book that claims to ~ "build iPhone apps using only HTML, CSS, and JavaScript", do you really want to assume/require the reader's server to have PHP, too?

  3. William Doane Posted 1 month, 4 days and 21 hours ago

    I've now read through chapter 6, and it seems that you do not leverage PHP at all, so far... so why not just make this index.html... I don't think you lose anything in the process and it makes the book accessible for those with non-scriptable servers.

  4. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Agreed. I'll update.

  5. Leslie Murphy Posted 13 days and 1 hour ago

    is this more accurate:

    it would read: “Get all of the ul elements from the #header element of index.html and use them to replace the current contents of the #container element of the current page.

Add a comment

4

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 loadPages() function and insert it into the #container element of the current page. When you're done, run the hijackLinks() function.”

5

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.

6

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

7

Normally, a web browser will navigate to a new page when a link is clicked. This navigation response is called the “default behavior” of the link. Since we are handling clicks and loading pages manually, we need to prevent this default behavior. On this line, I’ve done so by calling the built-in preventDefault() method of the event object. If I had left that line out, the browser would have dutifully left the current page and navigated to the url of clicked link.

8

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

Tip

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 “JavaScript: The Good Parts” by Douglas Crockford. 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 has read some stuff on the page and decides to click 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.

Tip

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. A good reference is located at http://www.w3schools.com/htmldom/dom_obj_event.asp.

4 comments

  1. William Doane Posted 1 month, 5 days and 20 hours ago

    properties of THE event object

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Thanks!

  3. Dave Nelson Posted 30 days and 8 hours ago

    When I go to the URL http://www.w3schools.com/htmldom/dom_obj_event.asp I am redirected to the URL http://www.w3schools.com/jsref/default.asp

    Is this the URL you are looking for? http://www.w3schools.com/jsref/dom_obj_event.asp

  4. Bhavesh Mehta Posted 27 days and 18 hours ago

    What Dave Nelson said.

Add a comment

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 does still leave quite a bit to be desired. Let’s slick things up a bit.

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. We need to provide some feedback to the user to let them know that something is, in fact, happening. Without this feedback, the user will 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).

Note

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

If you are using the Safari desktop browser to view the pages, you'll need to use your Mac's hostname or external IP address in the URL (for example, mymac.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).

2 comments

  1. Leslie Murphy Posted 13 days ago

    neat trick!

  2. Leslie Murphy Posted 13 days ago

    You also could mention that often real world apps are more affected by latency than bandwidth. I served as CTO at Shunra so I know a thing or two about that :-).

    A few tools worth mentioning for the serious developer interested in performance are the Charles web debugging proxy $50 and Shunra VE Desktop ($499). There is a very simple plugin for IE from Aptimize that is free. For MSDN users, there is also the upcoming Visual Studio 2010 which adds a new load test and unit test component, named Network Emulator FWIW The Android emulator also includes network latency testing.

Add a comment

Thanks to jQuery, providing this sort of feedback 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, “Adding a simple progress indicator to the page.” shows a modified version of Example 3.3, “This bit of JavaScript in iphone.js converts the links on the page to Ajax requests.”. The lines you need to add to iphone.js are shown in bold.

5 comments

  1. Andrew Posted 1 month, 6 days and 17 hours ago

    Be careful switching between "we" and "I", it can be jarring for readers. Earlier in the page you used "you." Pick one point of view and maintain it throughout.

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Agreed.

  3. Leon Cowle Posted 1 month, 3 days and 11 hours ago

    You say the lines you need are shown in bold, but they don't seem to be.

  4. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    Actually, scratch that. Because you don't seem to use bold lines anywhere where you're adding new lines to existing code(well, at least as far as I've read), why do so here?

  5. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    I'm sorry for the comment spam! But I have to correct my observation. I seems you DO use bold. However, on both Firefox and Internet Explorer (6), I couldn't really see the bold -- until now that I've looked very closely. It just seems that the font used for the code snippets doesn't lend itself to bold very well.

    Sorry about therefore making confusing comments!

Add a comment

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>');
    if (url == undefined) {
        $('#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);
    });
    $('#progress').remove();
}
            

See Example 3.5, “CSS added to iphone.css used to style the progress indicator.” for the CSS that needs to be added to iphone.css to style the progress div.

Example 3.5. CSS added to iphone.css used to style the progress indicator.

#progress {
    -webkit-border-radius: 10px;
    background-color: rgba(0,0,0,.7);
    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;
}
            

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

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

My site happens to have a single h2 at the beginning of each page that would make a nice page title (see Figure 3.2, “Before moving the page heading to the toolbar...”). You can see this in the HTML source shown in Chapter 2, Basic iPhone Styling. To be more iPhone-esque, I’m going to pull that title out of the content and put it in the header (see Figure 3.3, “...and after moving the page heading to the toolbar.”). Again, jQuery to the rescue: you can just add three lines to the hijackLinks() function to make it happen. Example 3.6, “Using the h2 from the target page as the toolbar title.” shows the hijackLinks function with these changes.

Figure 3.2. Before moving the page heading to the toolbar...

Before moving the page heading to the toolbar...

Figure 3.3. ...and after moving the page heading to the toolbar.

...and after moving the page heading to the toolbar.

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);
    });
    var title = $('h2').html() || 'Hello!';
    $('h1').html(title);
    $('h2').remove();
    $('#progress').remove();
}
            

2 comments

  1. William Doane Posted 1 month, 5 days and 20 hours ago

    Instead of a default of "Hello!", wouldn't "Main Menu" or some such be more appropriate?

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Hello! is friendlier ;-)

Add a comment


Note

Note that 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 would read: “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.

2 comments

  1. William Doane Posted 1 month, 5 days and 20 hours ago

    For clarity, should read: "The double pipe (||) in the first line of..."

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Agreed.

Add a comment

Note

This point probably needs some clarification. When the user first loads the iphone.html url, they are only going to see the overall site navigation elements, as opposed to any site content. They won't see any site content until they tap a link on this initial navigation page.

I have a few pages on my site that have titles that are longer than can fit in the header bar (Figure 3.4, “Text wrapping in the toolbar is not very iPhone-ish...”). I could just let the text break onto more than one line, but that would not be very iPhone-ish. Rather, I’ve updated the #header h1 styles such that long text will be truncated with a trailing ellipis (see Figure 3.5, “...but we can beautify it with a CSS ellipsis.” and Example 3.7, “Adding an ellipsis to text that is too long for its container.”). This might be my favorite little-known CSS trick.

Figure 3.4. Text wrapping in the toolbar is not very iPhone-ish...

Text wrapping in the toolbar is not very iPhone-ish...

Figure 3.5. ...but we can beautify it with a CSS ellipsis.

...but we can beautify it with a CSS ellipsis.

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 0px #fff;
    max-width: 160px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
            

2 comments

  1. Brian Lopiccolo Posted 1 month, 6 days and 23 hours ago

    What about Landscape mode for the iPhone?

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Brian - I get to that in chapter 4. Thanks!

Add a comment


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 of 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: ellipsis appends three dots to the end of any chopped off text to indicate to the user that she is not seeing the entire string.

Let’s say you have an About page that is longer than the viewable area on the iPhone. The user visits the page, scrolls down to the bottom, and clicks on a link to your Contact page. If you have more than a screenful of text on your Contact page, the new data will appear with the window still scrolled all the way to the bottom.

2 comments

  1. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    This paragraph had me baffled at first -- I thought it was a continuation of the previous paragraph (ellipsis), but turns out it seeems to be a new topic. Maybe add a header?

  2. Stefan Ladstätter Posted 26 days and 15 hours ago

    Agreed.

Add a comment

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, I have added a scrollTo() command to the loadPage() function (see Example 3.8, “It’s a good idea to scroll back to the top when a user navigates to a new page.”).

Whenever a user clicks a link, the page will first jump to the top. This has the added benefit of ensuring that 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);
    if (url == undefined) {
        $('#container').load('index.html #header ul', hijackLinks);
    } else {
        $('#container').load(url + ' #content', hijackLinks);
    }
}
            

Like most sites, mine has links to external pages (i.e. pages hosted on other domains). I don’t want to hijack these external links because it wouldn’t make sense to inject their HTML into my iPhone-specific layout. In Example 3.9, “You can allow external pages to load normally by checking the domain name of the url.”, I have added a conditional that checks the url for the existence of my domain name. If it’s found, the link is hijacked and the content is loaded into the current page; i.e. Ajax is in effect. If not, the browser will navigate to the url normally.

Caution

You must change jonathanstark.com to the appropriate domain or host name for your web site, or the links to pages on your web site will no longer be hijacked.

Example 3.9. You can allow external pages to load normally by checking the domain name of the url.

function hijackLinks() {
    $('#container a').click(function(e){
        var url = e.target.href;
        if (url.match(/jonathanstark.com/)) {
            e.preventDefault();
            loadPage(url);
        }
    });
    var title = $('h2').html() || 'Hello!';
    $('h1').html(title);
    $('h2').remove();
    $('#progress').remove();
}
            

5 comments

  1. Hank Sims Posted 1 month, 11 days and 16 hours ago

    External pages didn't work for me until I added an else clause to hijackLinks:

    if (url.match(/jonathanstark.com/)) { e.preventDefault(); loadPage(url); } else { document.open(url); }

  2. Jonathan Stark Posted 1 month, 10 days and 5 hours ago

    Hi Hank - I'm glad you got it working, but I'm sure you do not need to have else clause with the sample files for the book. Have you included any other javascript libraries? Are you sure you cleared your cache after making your changes? What browser did you test in?

  3. Hank Sims Posted 1 month, 10 days and 1 hour ago

    Jonathan: External pages worked fine for me on the desktop -- Firefox, Opera and Konquerer in Ubuntu -- but they did not work in Mobile Safari. The phone would simply reload the page on which the link was located. I did clear the cache.

    The only other javascript I had included was jQuery and jQTouch.

    I'm guessing, though, that you are right and the fault is due to some quirk in my setup, as I'm not all that well versed in browsers or js. Just thought I'd bring it to your attention in case it were a bug.

  4. Andrew Posted 1 month, 6 days and 17 hours ago

    It's worth pointing out that the match(/somedomain.com/) approach isn't very robust or generalizable. A better approach might be to use a jQuery plugin like this.

  5. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Hank - Oh, i just realized that you were probably bumping up against the cross domain javascript restriction. You should replace jonathanstark.com with your domain name. I've update the book to explain this.

Add a comment


Tip

The url.match function uses a language, regular expressions, that is often embedded within other programming languages such as JavaScript, PHP, and Perl. Although this regular expression is simple, more complex expressions can be a bit intimidating, but are well worth becoming familiar with. My favorite regex page is located at http://www.regular-expressions.info/javascriptexample.html.

Roll Your Own Back Button

The elephant in the room at this point is that the user has no way to navigate back to previous pages (remember that we've hijacked all the links, so the Safari page history won't work). Let’s address that by adding a back button to the top left corner of the screen. First, I’ll update the JavaScript, and then I’ll do the CSS.

Adding a standard iPhone-ized back button to the app means keeping track of the user’s click history. To do this, we’ll have to A) store the url of the previous page so we know where to go back to, and B) store the title of the previous page, so we know what label to put on the back button.

Adding this feature touches on most of the JavaScript we’ve written so far in this chapter, so I’ll go over the entire new version of iphone.js line by line (see Example 3.10, “Expanding the existing JavaScript example to include support for a back button.”). The result will look like Figure 3.6, “It wouldn’t be an iPhone app without a glossy, left-arrow back button.”.

Figure 3.6. It wouldn’t be an iPhone app without a glossy, left-arrow back button.

It wouldn’t be an iPhone app without a glossy, left-arrow back button.

Example 3.10. Expanding the existing JavaScript example to include support for a back button.

 var hist = [];1
 var startUrl = 'index.html';2
 $(document).ready(function(){3
     loadPage(startUrl);
 });
 function loadPage(url) {
    $('body').append('<div id="progress">Loading...</div>');4
    scrollTo(0,0);
    if (url == startUrl) {5
        var element = ' #header ul';
    } else {
        var element = ' #content';
    }
    $('#container').load(url + element, function(){6
        var title = $('h2').html() || 'Hello!';
        $('h1').html(title);
        $('h2').remove();
        $('.leftButton').remove();7
        hist.unshift({'url':url, 'title':title});8
        if (hist.length > 1) {9
            $('#header').append('<div class="leftButton">'+hist[1].title+'</div>');10
            $('#header .leftButton').click(function(){11
                var thisPage = hist.shift();12
                var previousPage = hist.shift();
                loadPage(previousPage.url);
            });
        }
        $('#container a').click(function(e){13
            var url = e.target.href;
            if (url.match(/jonathanstark.com/)) {14
                e.preventDefault();
                loadPage(url);
            }
        });
        $('#progress').remove();
    });
}            
            

5 comments

  1. William Doane Posted 1 month, 5 days and 18 hours ago

    at line 11 $('#header .leftButton').click(function(){
    you need the event parameter in the function definition $('#header .leftButton').click(function(e){

  2. William Doane Posted 1 month, 5 days and 18 hours ago

    at line 11

    $('#header .leftButton').click(function(){
    

    you need the event parameter in the function definition

    $('#header .leftButton').click(function(e){
    

    (just trying to improve the formatting of the code)

  3. William Doane Posted 1 month, 5 days and 18 hours ago

    ignore me.... it as line 13 I was focused on... and it looks like it's been fixed.

  4. William Doane Posted 1 month, 5 days and 18 hours ago

    OK... last comment on this point... I clarify, below, but... the issue is that at line 11 (yes 11) in this code, there is no event parameter... it gets added below. But the bold e in "(e)" was so subtle, I missed it the first dozen times through. Perhaps it should just be added at line 11 here, even though it's not yet needed. I suspect doing so will prevent a bevy of readers from having non-functional history buttons, and you could add a quick comment in your write-up explaining line 11.

  5. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Hmm... I think this code sample has changed since you made your comment, so the line numbers you gave don't match up anymore. I think I get your point though: you are pointing out that I am accepting the event in one click handler, but not the other. I'm not sure which is more confusing: to accept a parameter into a function, but never use it; or to have the two click handlers set up differently.

Add a comment


1

On this line, I’m initializing a variable named hist as an empty array. Since I’ve defined it outside of any functions, it exists in the global scope and will be available everywhere in the page. Note that I didn’t use the full word history as my variable name because that is a predefined object property in JavaScript and should be avoided in your own code.

2

Here I’m defining the relative url of the remote page to load when the user first visits iphone.html. You might recall from earlier examples that I just checked for url == undefined to handle the first page load, but in this example we are going to use the start page in a few places. Therefore, it makes sense to define it globally.

3

This line and the next make up the document ready function definition. Note that unlike previous examples, I’m passing the start page to the loadPage() function.

4

On to the loadPage() function. This line and the next are verbatim from previous examples.

5

This if...else statement determines which elements to load from the remote page. For example, if we want the start page, we grab the uls from the header; otherwise, we grab the content div.

6

On this line, the url parameter and the appropriate source element are concatenated as the first parameter passed to the load function. As for the second parameter, I’m passing an anonymous function (an unnamed function that is defined inline) directly. As we go through the anonymous function, you’ll notice a strong resemblance to the hijackLinks() function, which has been replaced by this anonymous function. For example, the following three lines are identical to previous examples.

7

On this line, I’m removing the .leftButton object from the page. This might seem weird because I haven’t yet added it to the page; we’ll be adding it a couple steps down.

8

Here I’m using the built-in unshift method of the JavaScript array to add an object to the beginning of hist array. The object I’m adding has two properties: url and title—the two pieces of information we need to support the back button display and behavior.

9

On this line, I’m using the built-in length method of the JavaScript array to find out how many objects are in the history array. If there is only one object in the history array, it means that the user is on the first page. Therefore, we don’t need to display a back button. However, if there is more than one object in the hist array, we need to add a button to the header.

10

Next, I’m adding that .leftButton I mentioned above. The text of the button will be the same as title of the page before the current page, which is what I’m accessing with the hist[1].title code. JavaScript arrays are zero-based, so the first item in the array (the current page) has an index of 0. In other words, index 0 is the current page, index 1 is the previous page, index 2 is the page before that, and so on.

11

In this block of code, I’m binding an anonymous function to the click handler of the back button. Remember, click handler code executes when the user clicks, not when the page loads. So, after the page loads and the user clicks to go back, the code inside this function will run.

12

This line and the next use the built-in shift method of the array to remove the first two items from the hist array, and then the last line in the function sends the url of the previous page to the loadPage() function.

13

The remaining lines were copied exactly from previous examples, so I won’t rehash them here.

14

This is the URL matching code introduced earlier in this chapter. Remember to replace jonathanstark.com with part of your web site's domain or host name, or none of the local links will be hijacked and loaded into the page.

Tip

Please visit http://www.hunlock.com/blogs/Mastering_Javascript_Arrays for a full listing of JavaScript array functions with descriptions and examples.

Now that we have our back button, all that remains is to purty it up with some CSS (see Example 3.11, “Add the following to iphone.css to beautify the back button with a border image.”). I start off by styling the text with font-weight, text-align, line-height, color, and text-shadow. I continue by placing the div precisely where I want it on the page with position, top, and left. Then, I make sure that long text on the button label will truncate with an ellipis using max-width, white-space, overflow, and text-overflow. Finally, I apply a graphic with border-width and -webkit-border-image. Unlike my earlier border image example, this image has a different width for the left and right borders because the image is made asymetrical by the arrowhead on the left side.

2 comments

  1. Ben Parizek Posted 1 month, 6 days and 17 hours ago

    Back button link in css is relative. Where can we find it?

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    You can get it out of jqtouch download at jqtouch.com. I have add this info in an earlier chapter as well. Thanks!

Add a comment

Note

Don't forget that you'll need an image for this button. You'll need to save it as back_button.png in the images folder underneath the folder that holds your HTML file. See the section called “Adding Basic Behavior with jQuery” for tips on finding or creating your own button images.

Example 3.11. Add the following to iphone.css to beautify the back button with a border image.

#header div.leftButton {
    font-weight: bold;
    text-align: center;
    line-height: 28px;
    color: white;
    text-shadow: rgba(0,0,0,0.6) 0px -1px 0px;
    position: absolute;
    top: 7px;
    left: 6px;
    max-width: 50px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-width: 0 8px 0 14px;
    -webkit-border-image: url(images/back_button.png) 0 8 0 14;
}
            

By default, Mobile Safari displays a translucent gray box over clickable objects that have been tapped (Figure 3.7, “By default, Mobile Safari displays a translucent gray box over clickable objects that have been tapped.”). This may appear only briefly, but removing it is easy and makes the app look much better. Since our back button is not rectangular, this effect looks a little lame. Fortunately, Mobile Safari supports a property called -webkit-tap-highlight-color that allows you to change the default to whatever color you like. I want to remove the highlight completely, which I’ve done here by setting the tap highlight to a fully transparent color (see Example 3.12, “Add the following to iphone.css to remove the default tap highlight from Mobile Safari.”).

Figure 3.7. By default, Mobile Safari displays a translucent gray box over clickable objects that have been tapped.

By default, Mobile Safari displays a translucent gray box over clickable objects that have been tapped.

2 comments

  1. Janos Rusiczki Posted 1 month, 24 days and 2 hours ago

    Screenshot has "messy" background.

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    That'll get fixed in production. Thanks!

Add a comment

Example 3.12. Add the following to iphone.css to remove the default tap highlight from Mobile Safari.

#header div.leftButton {
    font-weight: bold;
    text-align: center;
    line-height: 28px;
    color: white;
    text-shadow: rgba(0,0,0,0.6) 0px -1px 0px;
    position: absolute;
    top: 7px;
    left: 6px;
    max-width: 50px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-width: 0 8px 0 14px;
    -webkit-border-image: url(images/back_button.png) 0 8 0 14;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
}
            

2 comments

  1. Ben Parizek Posted 1 month, 6 days and 17 hours ago

    I've looked here (http://jonathanstark.com/images/) but don't find the back button.

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Sorry! Download jqtouch from jqtouch.com and look in the themes folder.

Add a comment


In the case of the back button, there could be at least a second or two of delay before the content from the previous page appears. To avoid frustration, I want the button to look clicked the instant it’s tapped. In a desktop browser, this would be a simple process; you’d just add a declaration to your CSS using the :active psuedo class to specify an alternate style for the object that was clicked. I don't know if it’s a bug or a feature, but this approach does not work on the iPhone; the :active style is ignored.

1 comment

  1. Chris Weekly Posted 27 days and 5 hours ago

    Typo, "psuedo"

Add a comment

I toyed around with combinations of :active and :hover, which brought me some success with non-Ajax apps. However, with an Ajax app like the one we are using here, the :hover style is sticky (i.e. the button appears to remain "clicked" even after the finger is removed).

2 comments

  1. Michael Korican Posted 1 month, 22 days and 23 hours ago

    3.13, "title" ???

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Fixed, thanks!

Add a comment

Fortunately, the fix is pretty simple. I use jQuery to add the class clicked to the button when the user taps it. I’ve opted to apply a darker version of the button image to the button in the example (see Figure 3.8, “It might be tough to tell in print, but the clicked back button is a bit darker than the default state.” and Example 3.13, “Add the following to iphone.css to make the back button looked clicked when the user taps it.”). You'll need to make sure you have a button image called back_button_clicked.png in the images subfolder. See the section called “Adding Basic Behavior with jQuery” for tips on finding or creating your own button images.

Figure 3.8. It might be tough to tell in print, but the clicked back button is a bit darker than the default state.

It might be tough to tell in print, but the clicked back button is a bit darker than the default state.

2 comments

  1. Michael Korican Posted 1 month, 22 days and 23 hours ago

    Examp;e 3.13. title ???

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Fixed, thanks!

Add a comment

Example 3.13. Add the following to iphone.css to make the back button looked clicked when the user taps it.

#header div.leftButton.clicked {
    -webkit-border-image: url(images/back_button_clicked.png) 0 8 0 14;
}
            

Note

Since I’m using an image for the clicked style, it would be smart to preload the image. Otherwise, the unclicked button graphic will disappear the first time it’s tapped while the clicked graphic downloads. I’ll cover image preloading in the next chapter.

2 comments

  1. Andrew Posted 1 month, 6 days and 17 hours ago

    Or image spriting, maybe?

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Andrew - Yep. don't want to open that can of worms though. Gurus will know to do this if they want to.

Add a comment

With the CSS in place, I can now update the portion of the iphone.js that assigns the click handler to the back button. First, I add a variable, e, to the anonymous function in order to capture the incoming click event. Then, I wrap the event target in a jQuery selector and call jQuery’s addClass() function to assign my clicked CSS class to the button:

2 comments

  1. Michael Korican Posted 1 month, 22 days and 23 hours ago

    See 3.10 lines 23, 24, 25: vars have different names

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    I can't find the discrepancy, so it looks like it's been fixed. PLMK if you still see a bug. Thanks!

Add a comment

$('#header .leftButton').click(function(e){
    $(e.target).addClass('clicked');
    var thisPage = hist.shift();
    var previousPage = hist.shift();
    loadPage(lastUrl.url);
});
        

4 comments

  1. Mike Boulet Posted 1 month, 22 days and 22 hours ago

    The following code:

    var thisUrl = hist.shift(); var lastUrl = hist.shift(); loadPage(lastUrl.url);

    should be:

    var thisPage = hist.shift(); var previousPage = hist.shift(); loadPage(previousPage.url);

  2. William Doane Posted 1 month, 5 days and 18 hours ago

    the addition of the event parameter here is very subtle... note well the mess I made of comments on this code block above... I missed the addition of the parameter addition. It might be better to just include it in example 3.10, even though it's not strictly needed there.

  3. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Mike - Oh, now I see what you mean. Fixed - thanks!

    @William - Yeah, I guess I agree. I added the e.

  4. Stefan Ladstätter Posted 26 days and 13 hours ago

    I also overlooked it, the bold typeface is very subtle. I am not sure it should be in the 3.10 example, though (someone seems to agree, the 'e' is still missing there).

Add a comment

Note

A special note to any CSS gurus in the crowd: the CSS Sprite technique–popularized by A List Apart–is not an option in this case because it requires setting offsets for the image. Image offsets are not supported by the -webkit-border-image property.

3 comments

  1. Andrew Posted 1 month, 6 days and 17 hours ago

    Nuts. I commented too soon above. Ignore please!

  2. William Doane Posted 1 month, 5 days and 19 hours ago

    suppored -> supported

  3. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Andrews - You're always a step ahead, it seems ;-)

    @William - Gratzie!

Add a comment

Adding an Icon to the Home Screen

Hopefully, users will want to add an icon for your webapp (called a “web clip icon”) to their home screens. They do this by tapping the plus button at the bottom of the Safari window (Figure 3.9, “Adding a web clip icon to your home screen, step 1: Click the plus button at the bottom of the Safari window.”), tapping Add to Home Screen (Figure 3.10, “Adding a web clip icon to your home screen, step 2: Click the “Add to Home Screen” button in the dialog.”), and clicking the Add button (Figure 3.11, “Adding a web clip icon to your home screen, step 3: Click the “Add” button in the “Add to Home” Screen panel.”). By default, iPhone will create this icon by thumbnailing the current page (including position and zoom), and applying rounded corners and a glossy effect (Figure 3.12, “Adding a web clip icon to your home screen, step 4: The 57 x 57 pixel image that you provided will show up on the home screen.”).

Figure 3.9. Adding a web clip icon to your home screen, step 1: Click the plus button at the bottom of the Safari window.

Adding a web clip icon to your home screen, step 1: Click the plus button at the bottom of the Safari window.

Figure 3.10. Adding a web clip icon to your home screen, step 2: Click the “Add to Home Screen” button in the dialog.

Adding a web clip icon to your home screen, step 2: Click the “Add to Home Screen” button in the dialog.

Figure 3.11. Adding a web clip icon to your home screen, step 3: Click the “Add” button in the “Add to Home” Screen panel.

Adding a web clip icon to your home screen, step 3: Click the “Add” button in the “Add to Home” Screen panel.

Figure 3.12. Adding a web clip icon to your home screen, step 4: The 57 x 57 pixel image that you provided will show up on the home screen.

Adding a web clip icon to your home screen, step 4: The 57 x 57 pixel image that you provided will show up on the home screen.

This might be fine for slackers, but the cool kids provide a custom web clip icon. The simplest way to do this is specify a single icon for your entire site by uploading a file named apple-touch-icon.png to your web root. The file should be 57 pixels square, and without gloss or rounded corners because the iPhone will add these automatically. If you don’t want the iPhone to add effects to your webclip icon, change the name of the file to apple-touch-icon-precomposed.png.

3 comments

  1. Andrew Posted 1 month, 6 days and 17 hours ago

    Darn it, again with "the cool kids?" What about professionals or perfectionists? Wouldn't we want to use a custom icon too?

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    @Andrew - Aw, c'mon... you know you're cool. Admit it... ;-)

  3. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    'slackers' might be a tad harsh :-)

Add a comment

In some cases, you might want to provide a web clip icon for a page that is different from the rest of your site. You can do this by adding one of the following lines to the head section of the "traffic cop" HTML document, iphone.html (you’d replace myCustomIcon.png with the absolute or relative path to the image):

<link rel="apple-touch-icon" href="myCustomIcon.png" />

<link rel="apple-touch-icon-precomposed" href="myCustomIcon.png" />

Note

If you are going to use precomposed images, make the corner radius 10 pixels or more. Otherwise iPhone will round the corners to 10 pixels. In either case, using precomposed does suppress the addition of the glossy effect.

Full Screen Mode

Feel like reclaiming a quarter of the available vertical space from Mobile Safari (104 pixels to be precise)? Add the following line to the head section of the "traffic cop" HTML document, iphone.html, and your web app will display in full screen mode when launched from the web clip icon:

3 comments

  1. William Doane Posted 1 month, 5 days and 19 hours ago

    "will in display full screen mode" --> "will display in full screen mode"

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    Dang, O'Reilly should offer you a job :)

  3. William Doane Posted 1 month, 1 day and 17 hours ago

    I'm listening... :) My students hate that I spot these things so easily in their papers.

Add a comment

<meta name="apple-mobile-web-app-capable" content="yes" />

2 comments

  1. Ellen Herzfeld Posted 3 days and 2 hours ago

    I followed this example step by step : added the icon to the home screen, then added the meta to the iphone.html file. Launching the app from the icon did not activate full screen mode. After scratching my head for a while I deleted the icon and tried again. This time it worked. I did this twice to be sure. Conclusion: the meta must be present when the icon is added to the home screen, otherwise full screen mode does not work. I have no idea why this is, but if you can reproduce it, you may consider adding a note about this.

  2. Jonathan Stark Posted 3 days and 1 hour ago

    Thanks Ellen! I heard a similar report from one other person, but I couldn't reproduce. If I ever get to the bottom of it, I'll post again.

Add a comment

I would’ve told you about this feature earlier, but it’s only useful once you have hijacked all of your hyperlinks with Ajax. As soon as a user clicks on a non-hijacked link—one that actually navigates to a new page—Mobile Safari will launch and load the page normally. This behavior is perfect for the example we’ve been working with because external links (Amazon, Twitter, Facebook, etc.) will open in Safari.

Changing the Status Bar

Once you’ve added the apple-mobile-web-app-capable meta tag, you have the option to control the background color of the 20 pixel status bar at the top of the screen using the apple-mobile-web-app-status-bar-style meta tag. The normal gray Safari status bar is the default, or you can change it to black (see Figure 3.13, “Full screen mode gives you about 25% more screen real estate (and looks way cooler).”). You can also set it to black-translucent which makes it partially transparent and additionally removes it from the document flow. In other words, your content will be shifted up by 20 pixels and behind the status bar the when page first loads, so you might have to position your header a little lower to compensate.

1 comment

  1. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    ...behind the status bar the when... ==> when the

Add a comment

<meta name="apple-mobile-web-app-status-bar-style" content="black" />

5 comments

  1. Ben Parizek Posted 1 month, 6 days and 16 hours ago

    hrm... this doesn't seem to work for me (just adding the two lines of code above). I've tried adding them both to the iphone.html and the index.html. Also, a quick google search reveals a lot of people that say these tags have a variety of problems.

  2. William Doane Posted 1 month, 5 days and 19 hours ago

    Same result here... no effect (iPhone 3g, latest public firmware)

  3. William Doane Posted 1 month, 5 days and 18 hours ago

    Ah HA! This needs clarification, but... this works ONLY when you've added an icon for the page to your home screen and accessed the page via that icon (i.e., web app mode, so that the above meta properties apply).

  4. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    I have added a note about full screen mode being required for status bar styles to take effect.

  5. Leon Cowle Posted 1 month, 3 days and 10 hours ago

    I would recommend your note being more explicit. "full screen mode" doesn't really explain that it needs to be launched from an icon on your springboard. Maybe something like: "Changes to the status bar style will only take effect when the app is launched in full screen mode, i.e. from an icon on the homescreen". Just a suggestion, though :-)

Add a comment

Figure 3.13. Full screen mode gives you about 25% more screen real estate (and looks way cooler).

Full screen mode gives you about 25% more screen real estate (and looks way cooler).

1 comment

  1. Andrew Posted 1 month, 6 days and 17 hours ago

    In addition to looking "way cooler", this also makes your web app look a little more like a native app, which seems relevent given your comparison of the two technologies earlier.

Add a comment

Note

Changes to the status bar style will only take effect when the app is launched in full screen mode.

1 comment

  1. Kedar Deshpande Posted 29 days and 6 hours ago

    Following the steps I added the home page button for the page and then added in the meta name for full screen mode and status bar but the changes did not show up even after launching from the home screen. The way I got it to work however was to add in the meta names and then create the shortcut and then launch it.

Add a comment

Providing a Custom Startup Graphic

When an app is launched in full screen mode, the user is presented with a screenshot of the app while the first page is loading. I’m not a fan of this because it looks like the app is ready to be interacted with, when in reality, tapping a link will do nothing. Furthermore, the screenshot is based on the last page from the user’s previous visit, scrolled to wherever they left off; not very attractive.

Fortunately, Mobile Safari allows us to define a startup graphic that will be displayed while the page is loading. To add a custom startup graphic, create a 320px x 460px PNG file and place it in the same directory with iphone.html. Next, add the following line to the head section of iphone.html (you’d replace myCustomStartupGraphic.png with the absolute or relative path to your image):

<link rel="apple-touch-startup-image" href="myCustomStartupGraphic.png" />

The next time we launch our app from the web clip icon, the default loading behavior will take place while the new custom graphic is downloaded. On the subsequent launch, the custom startup graphic will be displayed (Figure 3.14, “We can provide a custom startup graphic for an app launched in full screen mode.”).

Figure 3.14. We can provide a custom startup graphic for an app launched in full screen mode.

We can provide a custom startup graphic for an app launched in full screen mode.

What You’ve Learned

In this chapter, you’ve learned how to convert a normal website into a full screen Ajax application, complete with progress indicators, a native looking back button, and a custom web clip icon. In the next chapter, you’ll learn how to make your app come alive by adding native user interface animations. That’s right; here comes the fun stuff!

5 comments

  1. Mike Boulet Posted 1 month, 22 days and 22 hours ago

    I'm totally confused.. :) Was I supposed to reuse code from the last chapter? I think scattering code fragments throughout a chapter and then showing screen shots of pages that will not look like anything I'm working is very confusing and a little frustrating. At the end of this chapter I didn't have anything that looked like the screen shot. Breaking up the code into fragments that you then subsequent explain makes a lot of sense, but I think there needs to be a complete listing of the final html/js/css code. This way you can compare the listing against what you just created in the etxt editor.

  2. Jonathan Stark Posted 1 month, 3 days and 15 hours ago

    filenames and instructions have been sprinkled throughout this and previous chapters. Hopefully that will clear things up.

  3. Collin Leverette Posted 1 month, 3 days and 6 hours ago

    It would be nice to have a summary of all the html, css, and js code in its entirety at the end of each chapter so it can be referenced to the working model that was built.

  4. Pol Posted 1 month and 11 hours ago

    thank you Jonathan for this wonderful step by step thing. Unfortunetly It's my second day struggling (unsuccesfully) with this chapter. I'm in the search of your recommendation "JavaScript:The Good Parts", so that in the future I will not have to bother you.I uploaded the code to my hosting, if you can please take a look at it I would appreciate it.

    http://www.polmoneys.com/apps/iphone.html

    thanks

  5. Stefan Ladstätter Posted 26 days and 12 hours ago

    I followed this and the previous chapter, and my site looks fine. My only (very minor) gripe is that the screenshots don't match the HTML provided.

    I am also a little disappointed that the button colors from the jQtouch package (version 1, beta 2) are way darker than those from the screenshots (which I like more). Also, their back_button_clicked.png looks nearly identical to the non-clicked version, thus I didn't notice at first that my code was working. There is really nothing you can do about that, though :-)

Add a comment

You must sign in or register before commenting

Atom Icon Comments on this page or Comments on the whole book.