PhoneGap From Scratch: App Template

PhoneGap From Scratch: App Template

Tutorial Details
  • Technology: PhoneGap
  • Difficulty: Beginner
  • Completion Time: 45 - 60 Minutes
This entry is part 3 of 5 in the series PhoneGap From Scratch

Want to learn how to use PhoneGap, but don’t know where to get started? Join us as we put together “Sculder”, not only a tribute to an excellent science fiction TV series, but a fully-fledged native mobile application for the believer in you!

In the last two parts we have installed Phonegap, gotten a hello world running, and then taken a look at some of the functionality that Phonegap offers us. Over the next two parts, we will build our app and take a look at some of the challenges that might arise in doing so.


Planning The Application

Before we get down to code, we are going to have to a little bit of planning. This way we know exactly what we want to app to achieve and can have a good idea of the user experience. You might achieve some great work from just hitting the code straight away (something I do all the time), but even just a little bit of planning beforehand can go a long way. Thinking through scenarios in your head will allow you to deal with problems early on.

First, we need to know what core features of the application will be, how they will be accessed, and how they will be used. The app we are going to build will have the following features:

  • Find a UFO Sighting by Location
  • Take a Sighting Photo
  • List a Twitter feed of UFO Sightings

If you’d like to do wireframes for your projects, they’re always good to make sure you know what you want your application to do and also roughly what you want it to look like. If the application is going to be large and have a lot of functionality, I would do some wireframes, but in this case I think that the application is small and so we can sort out the pretty basic functionality as we build the app.

If you do want to create wireframes, there are some pretty good tools out there to help you.

Mockingbird is an online tool where you can create the wireframes in the browser. You can try it out here

Balsamiq is an Adobe Air application with a whole load of templates and elements. It has a sketchy style much like Mockingbird too. You can download it here.

Omnigraffle is a mac only application which you can get a 14-day free trial here. Omnigraffle is my personal favorite and there are plenty of templates on Graffletopia for you to use.


Designing The Application

Look and feel is an important aspect of any application. When it comes to mobile applications, it’s an area that many often get wrong (mostly due to lack of understanding). I’m not a designer, and I don’t pretend to be one, so if I don’t have a designer at hand to do the designs, I will usually just design in the browser as I code (or on the device in this case).

If you want to make sure your app looks and feels good to use (and you aren’t one) hire one! Like a developer, good designers cost money. While you might not want to fork out for something you might be able to do yourself, it’s best to have someone who understands design to do this for you. Your app will be a hundred times better for doing so!

There have been some great design tutorials here on Mobiletuts+, which will give you a great understanding of what is required when diving into design for mobile.

One of the most important considerations for your design is the amount of images you use. If your design is going to require you to chop up PSD files and use lots of images – something is wrong – we want to do as much as possible with CSS and as we are building for pretty modern browsers, we can harness to power of many newer CSS3 techniques for the desired output. When you begin using lots of images, it begins to effect the performance of the application. When we have to use images, which is likely, we should build up a sprite (one image file containing all of our imagery) to save HTTP requests. It’s also possible to cache images on the device or use local storage too.


Markup for Mobile

Now is the time to get into actually coding the HTML, CSS, and Javascript for our application.

We will begin with some pretty basic HTML.

<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes" />
  <title>Sculder</title>
</head>
<body>
</body>
</html>

CSS support on mobile is good, but it’s not great. While a lot of common styles are supported, there are some “gotcha’s” as well. One of my favourite resources for checking support is caniuse.com. While the site doesn’t include every browser for mobile, it is a useful reference.

Go ahead and create a new blank stylesheet and include it in the head of your html. As you may already do with your desktop browser apps, set up some basic reset styles.

html, body{
 margin: 0;
 padding: 0;
 border: 0;
}
body {
 font-family:Arial,  sans-serif;
 line-height:1.5;
 font-size:16px;
 background: #fff;
 padding:5px;
 color: #000;
 word-wrap: break-word;
 -webkit-text-size-adjust: none;
}
h1, h2, h3, h4, h5, h6{ font-weight: normal; }
p img { float: left; margin: 0 10px 5px 0; padding: 0; }
img { border: 0; max-width: 100%; }

Next, we are going to add some basic HTML – a header and a some tab controls.

<header>
    <h1>Sculder</h1>
</header>
<div id="wrapper">
    <div id="main-content">
	</div>
</div>
<footer>
</footer>

First, I am going to give the background a colour and then style the header and footer. I’m going to go ahead and lay them out in a pretty average way, fixing them to the top and the bottom of the screen and using iScroll to give us the ability to overcome the lack of support for position:fixed in most devices.

/*	Main Styles */
body{
	background: #222;
}
header{
    top:0; left:0;
    height:45px;
}
footer {
    bottom:0; left:0;
    height:48px;
    background-color:#c27b00;
    border-top:1px solid #eee;
}
header, footer{
	background-color:#deb500;
    padding:0;
	position:absolute;
    z-index:2;
    font-size:20px;
    width:100%;
    text-align:center;
    color:#f3f3f3;
    font-weight:bold;
    text-shadow:0 -1px 0 rgba(0,0,0,0.5);
    line-height:45px;
}
h1{
	margin:0;
	text-transform: uppercase;
}
#wrapper {
    position:absolute;
    z-index:1;
    top:45px; bottom:48px; left:0;
    width:100%;
    overflow:auto;
}
#main-content {
    position:absolute;
    z-index:1;
    width:100%;
    padding:0;
}

Next, we’re going to download iScroll 4 from here, and include the iscroll-lite.js file. This gives us the basic functionality that we will need. We will also make a app.js file to handle all of our JavaScript functionality. Include these files in the head of your index.html.

<link rel="stylesheet" href="css/style.css">
<script src="js/iscroll-lite.js"></script>
<script src="js/app.js"></script>

In our Javascript file we need to init iscroll.

var theScroll;
function scroll(){
	theScroll = new iScroll('wrapper');
}
document.addEventListener('DOMContentLoaded', scroll, false);

Now we are going to add our tab bar in our footer.

<ul id="tab-bar">
	<li>
		<a href="#">Map</a>
	</li>
	<li>
		<a href="#">Camera</a>
	</li>
	<li>
		<a href="#">Twitter</a>
	</li>
</ul>

We are going to style our tab bar so that each element takes up a third of the space available.

#tab-bar{
    margin:0;
    padding:0;
}
#tab-bar li {
    display: inline;
    float:left;
    width: 33.333%;
}
#tab-bar a {
    color: #cdcdcd;
    display: block;
    font-weight: bold;
    overflow: hidden;
    position: relative;
    text-align: center;
    text-decoration: none;
    -webkit-touch-callout:none;
}

All of this is pretty average CSS that you might use in a webpage. The only one that might be the odd one out to you is the -webkit-touch-callout:none. This rule is to stop the default action when a user holds down a link.

Thats all we’re going to be doing with the tab bar, but you may want to add icons to yours, much like most apps have. Just remember to make sure you use sprites for you images to keep the hit on performance minimal.


Paging

Now that we have the skeleton of our Application ready, we can look into paging through our app. There are a number of good ways to do this. One is to load everything up-front and then hide the pages we don’t want to show at first, then on a click of a link we can use CSS to slide a new container into view. Another option is to use Ajax and make an Ajax request for a HTML file and then slide that into view. As this is a relatively small application there is no harm in loading everything up-front and using CSS for the bulk of the page handling, much like jQuery Mobile does.

First we will set up some pages inside our main container.

<div id="pages">
	<div id="map" class="current">
	</div>
	<div id="camera">
	</div>
	<div id="twitter">
	</div>
</div>

Then make sure that the links href corresponds to these.

<ul id="tab-bar">
	<li>
		<a href="#map">Map</a>
	</li>
	<li>
		<a href="#camera">Camera</a>
	</li>
	<li>
		<a href="#twitter">Twitter</a>
	</li>
</ul>

Now we will hide all the pages and just display the page with the current class.

#pages > div {
    display: none;
}
#pages > div.current {
    display: block;
}

Next, we have to dive into some JavaScript. I’m going to go ahead and use jQuery for the rest of the project (not jQuery mobile). If you like, you can jQuerify the iScroll init (or not). Make sure to drop the minified version of jQuery into your JS directory as well.

First, I am going to bind a click to the anchors in the tab-bar, as we are using jQuery 1.7 we can use the new .on() function. We will then prevent the default action of a link, store the href of the clicked link in a nextPage variable, remove the current class from the current page and finally add the class to the clicked page.

$('#tab-bar a').on('click', function(e){
	e.preventDefault();
	var nextPage = $(e.target.hash);
	$("#pages .current").removeClass("current");
	nextPage.addClass("current");
});

You will now have some very basic paging as the content divs will now hide and show when the links are clicked.

This is and will work absolutely fine, but to add a little extra to the paging we are going to use some CSS and make the transition between pages a bit nicer with a basic fade in, fade out.

First we need to make sure that our pages are positioned in a way that gives us some control over their display. Add the following CSS.

#pages{
    position:relative;
}
/* Update this style */
#pages > div {
    display: none;
    position: absolute;
    top:0;left:0;
    width:100%;
}

Now we are going to use CSS animation to animate the page transition. First we start with some generic setting for the animation function and the timing.

.in, .out {
    -webkit-animation-timing-function: ease-in-out;
    -webkit-animation-duration: 400ms;
}

The we set the keyframes for the animation of the keyframes.

@-webkit-keyframes fade-in {
    from { opacity: 0; } to { opacity: 1; }
}

Lastly we set the Z-index to make sure that the page we just faded in is on-top.

.fade.in {
    -webkit-animation-name: fade-in;
    z-index: 5;
}
.fade.out {
    z-index: 0;
}

Now we need to add some more JavaScript to add and remove the CSS classes on click. First we need a function that will take a parameter, which will be the page to navigate to, then it will add the classes necessary to fade the new page in and bind a function to webkitAnimationEnd, this will then remove the necessary classes. The function looks like this.

function page(toPage) {
	var toPage = $(toPage),
	fromPage = $("#pages .current");
	toPage.addClass("current fade in").one("webkitAnimationEnd", function(){
		fromPage.removeClass("current fade out");
		toPage.removeClass("fade in")
	});
	fromPage.addClass("fade out");
}

If you have never seen the jQuery method .one() before, check out the documentation its just a way of binding and then unbinding.

If you test now on your device, you will notice that if you click the current page link, it will fade-out. We need to put in an if statement before we run our page function to make sure that we are not already on the current page – if we are we can just return out of the function.

function page(toPage) {
	var toPage = $(toPage),
	fromPage = $("#pages .current");
	if(toPage.hasClass("current") || toPage === fromPage) {
		return;
	};
	toPage.addClass("current fade in").one("webkitAnimationEnd", function(){
		fromPage.removeClass("current fade out");
		toPage.removeClass("fade in")
	});
	fromPage.addClass("fade out");
}

Now you can dump some text into each page container and test out on a device.

PhoneGap From Scratch - Demo

Conclusion

We’ve now begun building a web application and gotten our layout and paging working. In the next part we will start building out our pages, debugging on the mobile and get our Phonegap integration started.

Series Navigation«PhoneGap From Scratch: Device APIsPhoneGap From Scratch: Twitter & Maps»

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://blog.no-panic.at Florian Beer

    Thanks for this awesome tutorial!
    I replicated everything, but my pages won’t scroll.
    Header & Footer are fixed, but if I try to scroll the content always snaps back. Do I need to dynamically set the height of the pages div’s or something like that?

    Keep up the good work!

  • http://www.jsxtech.com Jaspal Singh

    Looking for next post.
    Thanks for sharing.

  • http://www.mmj-innovativ.de Marvin

    Please consider that the iScroll-Solutions have massive problems with Forms. It gets very jumpy when you focus an input-field. From my personal experience I would suggest to use a mobile framework like jQuery Mobile or Sencha Touch instead.

    • http://www.shaundunne.com Shaun
      Author

      Thanks for comment Marvin and yes, this is correct – iScroll has its limitations, especially when it comes to forms.

      Thankfully we will not have any forms in this example – but if you are going to use forms, JQM, Sencha and Titanium are great alternatives.

  • Mike

    Hi, Thanks for the great tutorial.

    I’ve followed your instructions, but I cannot get the pagination to work. When I click on the tabs on the bottom, it does nothing. The only screen that shows is the original current class.

    It might be because I do not know where to put “$(‘#tab-bar a’).on(‘click’, function(e){
    e.preventDefault();
    var nextPage = $(e.target.hash);
    $(“#pages .current”).removeClass(“current”);
    nextPage.addClass(“current”);
    });”

    I’ve put it in a script tag in the html and in the app.js file, but it does not work.

    Any ideas what I’m doing wrong?

    Thanks!

    • http://www.shaundunne.com Shaun
      Author

      Hi Mike,

      Where abouts is the include for your JS? If it’s in the head of your document you will have wait for the DOM to be ready before any JS is able to execute using,

      $(document).ready(function(){

      // execute functions on load go here

      });

      Alternatively – stick you JS include at the bottom of the document – before the closing body tag and it should be fine.

      • MeltingDog

        I too am having this issue. I have tried at booth the top and bottom of my pages to no avail.

        Any help you could offer would be greatly appreciated!

        function onBodyLoad()
        {
        document.addEventListener(“deviceready”, onDeviceReady, false);

        }

        function onDeviceReady()
        {

        //navigator.notification.alert(“Cordova is working”)

        }

        $(document).ready(function() {

        $(‘#tab-bar a’).on(‘click’, function(e){
        e.preventDefault();
        var nextPage = $(e.target.hash);
        $(‘#pages.current’).removeClass(‘current’);
        });
        });

        <!–Waiting for accelerometer…–>

        Map

        Camera

        Twitter


        Map

        Camera

        Twitter

  • Søren Laursen

    Hi.
    Great guide, which I enjoy regarding the part about transition and basic markup.
    Easy, simple and clean!

    I couldn’t get the last fade-in/out script to work, but found out that, you’ve “forgot” to call the function in your jQuery-part.

    Solution:

    $(‘#tab-bar a’).on(‘click’, function(e){
    e.preventDefault();
    var nextPage = $(e.target.hash);
    page(nextPage); //You need to add this for it to work
    $(“#pages .current”).removeClass(“current”);
    nextPage.addClass(“current”);
    });

    • Paul

      I´ve got the hole code inside of de app.js and it is still not working. any ideas?

      $(‘#tab-bar a’).on(‘click’, function(e){
      e.preventDefault();
      var nextPage = $(e.target.hash);
      page(nextPage); //You need to add this for it to work
      $(“#pages .current”).removeClass(“current”);
      nextPage.addClass(“current”);
      });

      function page(toPage) {
      var toPage = $(toPage),
      fromPage = $(“#pages .current”);
      if(toPage.hasClass(“current”) || toPage === fromPage) {
      return;
      };
      toPage.addClass(“current fade in”).one(“webkitAnimationEnd”, function(){
      fromPage.removeClass(“current fade out”);
      toPage.removeClass(“fade in”)
      });
      fromPage.addClass(“fade out”);
      }

      var theScroll;
      function scroll(){
      theScroll = new iScroll(‘wrapper’);
      }

      document.addEventListener(‘DOMContentLoaded’, scroll, false);

      • http://www.frankie.bz Frank Neulichedl

        Just move the includes to the bottom of index.html right before instead of including them in

      • Amit

        i have same issue like you. Any solution yet ??

  • peterm

    You can save yourself a lot of frustration if you change the body background to white. I plugged some text into see the paging, but it was black on black… That caught me up for a couple of hours…

  • Dan

    Hi,

    “Another option is to use Ajax and make an Ajax request for a HTML file and then slide that into view”

    Could you give an example of how to implement this?

    Currently looking to remove the gaps between pages (separate html files) that currently occur within my phonegap app.

    Thanks in advance and thanks for a great tutorial series.

  • mathew

    on click of the footer links, nothing happens !!!

  • http://www.yadahcl.com Jorge FG

    Great tutorial! I´m getting started with Phone Gap and this makes it very easy.

    Thanks!

  • http://www.mikeellan.com Mike Ellan

    This code is structured in such a way that the fade out never has a chance to execute. The below will result in both the fade in and out rendering properly.

    function page(toPage) {
    var toPage = $(toPage),
    fromPage = $(“#pages .current”);
    // Halt on existing item click
    if(toPage.hasClass(“current”) || toPage === fromPage) {
    return;
    };
    // Fade out existing page, on complete fade in destiation content
    $(“#pages .current”).addClass(“fade out”).one(“webkitAnimationEnd”, function(){
    $(this).removeClass(“current fade out in”);
    toPage.addClass(“current fade in”).one();
    });
    };

    // Primary navigation
    $(‘#tab-bar a’).on(‘click’, function(e){
    e.preventDefault();
    // Grab and pass existing and destination pages
    var nextPage = $(e.target.hash);
    page(nextPage);
    });

    • Choon

      I tried this…didn’t work for me though.

  • Sarah

    I still can’t get this to work with the latest release of PhoneGap.

    • Choon

      For those who got this working, will it be possible to share your source code? Would like to determine what I’m doing wrong :(

  • http://twitter.com/farmmangoes farmmangoes

    Nice tutorial , i want to design one ecommerce app like http://farmmangoes.com , can any tell it is possible in phonegap

  • Ryan

    The links at the bottom of my page aren’t working :/