Book Image

HTML5 iPhone Web Application Development

By : Alvin Crespo
Book Image

HTML5 iPhone Web Application Development

By: Alvin Crespo

Overview of this book

<p>Create compelling web applications specifically tailored for distribution on iOS Safari. Work through real world examples with references, and in-depth discussions on the approach; including its benefits and drawbacks.<br /><br />"HTML5 iPhone Web Application Development" strives to teach all levels of developers, beginners and professionals, the process of creating web applications for iOS Safari. Utilizing current industry standards for frontend development, learn to take advantage of HTML5, CSS3 and JavaScript to create compelling software.<br /><br />Start with reviewing current industry standards for frontend development, and end with creating a native application using the same codebase.</p> <p>Your journey will begin with an overview of current industry standards for frontend technology, quickly moving to solve real world issues; from creating a resizable or responsive gallery, to creating a single page application that utilizes the popular Backbone.js framework.</p> <p>"HTML5 iPhone Web Application Development" aims to make you an expert in developing web applications for the iOS Safari platform.</p>
Table of Contents (17 chapters)
HTML5 iPhone Web Application Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Establishing our JavaScript architecture


When establishing a JavaScript architecture for your application, there's a lot to think about, including possible changes in the near or short term, security, ease of use and implementation, documentation, and more. Once we can answer the various questions we have, we can then decide on the pattern (module, facade and/or mediator, and so on). We also need to know what library or framework would be best suited for us, such as jQuery, Zepto.js, Backbone.js, or Angular.js.

Luckily for us, we'll be keeping it plain and simple in order to deliver an effective application on an iPhone. We'll be utilizing Zepto.js as our supported library to keep it light. We'll then build upon Zepto by creating a custom JavaScript framework that follows a modular pattern.

Structuring our app functionality

First, let's open up our application directory in our preferred text editor.

Next, open the App.js file we created earlier within our JavaScript directory. The App.js file should be completely empty, and it shouldn't be included anywhere. This is where we will begin writing our framework.

Namespacing our application

If you're new to JavaScript, you have most likely created most of your code in the global scope—perhaps laying out most of your JavaScript inside of script tags. Although this may achieve some of your goals, when working on large scale applications we want to avoid such practices. Some of the reasons we want to namespace our applications is for maintainability, efficiency, and portability.

Let's start out by checking for the App namespace; if it exists we'll use what's there, if it does not exist, then we'll make an empty object. The following code shows how we can achieve this:

var App = window.App || {};

Immediately Invoked Function Expressions

Great! We are checking for the App namespace, now let's define it. Let's include the following code after the check:

App = (function(){}());

The previous code is doing several things, let's take it one step at a time. First, we're setting the App namespace to what is known as an Immediately Invoked Function Expression (IIFE). We are essentially creating a function that is wrapped by parentheses and immediately invoking it after the closing brace.

When we use the previous technique, or IIFE, we create a new execution context or scope. This helps in creating self-containing code that will hopefully, not impact other code on the site. It protects us and helps us follow the modular pattern efficiently.

Let's extend the previous functionality by passing in the window, document, and Zepto objects, as follows:

App = (function(window, document, $){
}(window, document, Zepto));

I know that this may be a bit confusing, but let's take a second to think through what we're doing here. First, we are setting some parameters in the function named window, document, and $. Then, we are passing in window, document, and Zepto when we invoke this method. Remember, we discussed previously that this creates a new scope or execution context? Well, this becomes useful to us because we can now pass in references to any object that might be global.

How is this useful to us? Well, imagine if you wanted to use the actual Zepto object over and over again it would be kind of tiring. It's not that difficult to type Zepto, but you can just namespace it to the dollar sign and keep it simple.

Use strict

Ok, so we've got our module setup. Now let's continue to extend it by including the use strict directives:

App = (function(window, document, $){
    'use strict';
}(window, document, Zepto));

This directive helps us debug our applications by making changes to how JavaScript runs, allowing certain errors to be thrown instead of failing silently.

Default options

Default options are a great way of giving your codebase some extensibility. If, for example, we want to customize or cache an element related to the application itself then following are the defaults we will use:

var _defaults = {
'element': document.body,
    'name': 'App',
    'videoOptions': {},
    'audioOptions': {},
    'touchOptions': {},
    'formOptions': {},
    'locationOptions': {},
    'singlePageOptions': {}
};

Let's look at these defaults briefly. First we will create a defaults variable, which will contain all the defaults for our application(s). Inside it, we have defined a default location to be referenced for our application with the 'element' default set to document.body—which gets our body element in DOM (Document Object Model). We then create a custom name for our application called 'App'. After this, we create empty objects for our video, audio, touch, form, location, and single page applications—to be built later. These empty objects will be extended as we continue through the book.

Defining the constructor

Now we need to define our constructor after the use strict directive. This constructor will take a single parameter named options. We will then extend the defaults with the parameter options and store these settings that can be retrieved later, if needed. We will then ultimately cache the 'element' option as a Zepto object.

function App(options) {
    this.options = $.extend({}, _defaults, options);
    this.$element = $(this.options.element);
}

Here is what the previous code is accomplishing. First, we are using the keyword this, which is a reference to what will be, an instance of App itself. Thus, this is the context of the object itself. Hopefully, this is not too confusing and will become clear as we go on. In this case, we are using this to define an object options, which will contain the merged contents of _defaults and any custom options we pass into the constructor.

Note, when we pass an empty object, or {} into $.extend() as the first parameter, we are telling Zepto to merge _defaults and options into a new object, thus not overwriting the _defaults object. This is useful when we need to do some sort of check in the future with the default options.

Once we've defined the options, we then cache the element with this.$element, where $ in front of element is just for my reference so that I immediately recognize a Zepto object versus a plain JavaScript object.

The prototype

Ok, so we've created our App namespace, constructed an IIFE to contain our code and defined our constructor. Now, let's start creating some public methods that can be accessed to make this a bit modular. But before we do that, let's try to understand JavaScript's prototype.

Think of prototype as a live object that can be accessed, modified, and updated whenever and however you like. It can also be thought of as a pointer, because JavaScript will continue to go down the chain until it finds the object or it will return undefined. The prototype is simply a way of extending functionality to any non-plain object.

To make things a bit more confusing, I mentioned that non-plain objects have prototypes. These non-plain objects would be Arrays, Strings, Numbers, and so on. A plain object is one where we simple declare an empty object as follows:

var x = {};

The x variable does not have a prototype, it is simply there as a key/value storage similar to our _defaults object.

If you haven't yet understood the prototype, don't worry, it's all about getting your hands dirty and getting some experience. So, let's keep moving and getting our applications to work.

At this point, your App.js file should look like the following:

var App = window.App || {};
App = (function(window, document, $){
    'use strict';
    var _defaults = {
        'element': document.body,
        'name': 'App',
        // Configurable Options for each other class
        'videoOptions': {},
        'audioOptions': {},
        'touchOptions': {},
        'formOptions': {},
        'locationOptions': {},
        'singlePageOptions': {}
    };
    function App(options) {
        this.options = $.extend({}, _defaults, options);
        this.$element = $(this.options.element);
    }
}(window, document, Zepto));

Defining public methods

Now we need to create some public methods by typing into the prototype. We'll create a getDefaults method, which returns our default options; toString will overwrite the native toString method so we can return a custom name. Then we'll create initialization methods to create our other applications, and we'll name these initVideo, initAudio, initLocalization, initTouch, initForms, and initSinglePage respectively.

App.prototype.getDefaults = function() {
    return _defaults;
};

App.prototype.toString = function() {
    return '[ ' + (this.options.name || 'App') + ' ]';
};

App.prototype.initVideo = function() {
    App.Video.init(this.options.videoOptions);
    return this;
};

App.prototype.initAudio = function() {
    App.Audio.init(this.options.audioOptions);
    return this;
};

App.prototype.initLocalization = function() {
    App.Location.init(this.options.locationOptions);
    return this;
};

App.prototype.initTouch = function() {
    App.Touch.init(this.options.touchOptions);
    return this;
};

App.prototype.initForms = function() {
    App.Forms.init(this.options.formOptions);
    return this;
};

App.prototype.initSinglePage = function() {
    App.SinglePage.init(this.options.singlePageOptions);
    return this;
};

At this point we have several methods we can access publicly when we create an instance of App. First, let's review the code we implemented previously, specifically this line that gets duplicated, but customized based on the init method:

App.Touch.init(this.options.touchOptions);

For every init method we have created a call to the appropriate application, for example, App.Touch, App.Forms, App.Video, and so on. Then we pass it the options we've defined in the constructor that merged our defaults, for example, this.options.touchOptions, this.options.formOptions, this.options.videoOptions, and so on.

Note, we haven't created these classes yet for Video, Forms, Touch, and others, but we will be creating these soon.

Returning our constructor/function

The last thing we need to do in App.js includes returning the constructor. So, after all the public methods defined previously, include the following code:

return App;

This bit of code, although simple, is extremely important. Let's look at a stripped down version of App.js to better understand what's going on:

App = (function(){
    function App() {}
    return App;
}());

As mentioned earlier, we are creating an App namespace that gets set to the immediately invoked function expression. When we do this, we create a new scope inside this function.

This is why we can have a function or constructor with the name App as well and have no conflicts or errors. But if you recall, our function App is also an object, just like everything in JavaScript is an object. This is why, when we return our function App the App namespace gets set to the constructor. This then allows you to create multiple instances of App, while centralizing your code inside of a new scope that is untouchable.