Dynamic Page Loading for PhoneGap

Untitled Document

Dynamic Page Loading for PhoneGap

Introduction


Mobile PhoneGap applications vary in complexity and size from simple, one-screen applications to large, multi-screen apps. To simplify development of applications that have more than one screen, JavaScript frameworks such as Dojo, jQuery or jQTouch can be used. They provide a way to display one screen while hiding the remaining screens. This is accomplished by using a single HTML file containing div elements for each screen. The current screen is made visible by setting it's css style properties. As the application grows to include more screens and function, the HTML file also grows in size. Since mobile devices have limited resources, larger HTML files load and run slower - particularly upon application launch.
 
To improve application performance, dynamic page loading can be used. For an application that uses dynamic page loading, the app launches and loads only the first screen. Based upon user interaction a new screen can be displayed by retrieving the new HTML content and replacing the current screen with it.
 
This blog post will describe how a PhoneGap mobile application can be structured to utilize dynamic page loading. Source code is provided for readers who want to build and run the application.
 

Dynamic Page Loading


The main HTML page for our application contains a div element to display dynamic content. It also loads the JavaScript code used by our application. Referring to the HTML code below, the container div is where we will place our content.
 
<body id="body" onload="init();">
    <h3 id="header">PhoneGap Dynamic Pages Demo</h3>
    <div id='container'></div>
</body>

 
The content is retrieved using XHR (also referred to as Ajax) from the same location that our HTML page was loaded.
 
PhoneGap applications typically load their main HTML page from the device; thus, XHR can access both local files or server files. However, if the main HTML page is loaded from a server, then only XHR requests to that server are allowed. This is a security restriction to prevent cross-domain security concerns.
 
The loadPage() function makes an XHR call to load the specified url. Once the content is available, the container div is updated by setting it's innerHTML property.
 
/**
 * Load page into url
 *
 * @param url           The url to load
 */
function loadPage(url) {
    var xmlhttp = new XMLHttpRequest();

    // Callback function when XMLHttpRequest is ready
    xmlhttp.onreadystatechange=function(){
        if (xmlhttp.readyState === 4){
            if (xmlhttp.status === 200) {
                document.getElementById('container').innerHTML = xmlhttp.responseText;
            }
        }
    };
    xmlhttp.open("GET", url , true);
    xmlhttp.send();
}

 
The final step is to specify the initial content to be displayed. This is done inside the init() function, which is run after the HTML page is loaded (defined by the onload property of the body element).
 
/**
 * Function called when page has finished loading.
 */
function init() {

    // Load first page into container
    loadPage("screen1.html");
}

 

Application Screens


With our dynamic loading function completed, it's time to write the screens that comprise our application. There is an HTML file for each screen in the app. Navigation between screens is accomplished by calling loadPage() from within a screen, or using global navigation links from outside the container div as shown below.
 
<body id="body" onload="init();">
    <h3 id="header">PhoneGap Dynamic Pages Demo</h3>
    <div id='container'></div>
    <a href="screen1.html">Screen 1</a>
    <a href="screen2.html">Screen 2</a>
</body>

 
To navigate from within a screen, loadPage() can be called from an event handler. For example, screen1 can load screen2 using the onclick button event.
 
<h3>Screen1.html</h3>
This is screen1.html.<br>
<button type="button" onclick="loadPage('screen2.html')">Load screen 2</button>

 
You don't need to specify the head or body tags in the screen files, since the content is HTML and will be set to the container's innerHTML.
 

Running JavaScript


When setting innerHTML with content, none of the embedded script tags are run - only the HTML content is rendered. To execute JavaScript when a new screen is loaded the JavaScript function must already exist. It can be included as part of the main HTML file for an application, or be located in a script file that is loaded by the main HTML file.
 
<script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script>
<script type="text/javascript" charset="utf-8" src="screen3.js"></script>
<script type="text/javascript" charset="utf-8">

/**
 * Load page into url
 *
 * @param url           The url to load
 * @param onleave       The function to call before leaving
 * @param onenter       The function to call after loading
 */
function loadPage(url, onleave, onenter) {

    // If onleave function specified
    if (onleave) {
        onleave();
    }

    var xmlhttp = new XMLHttpRequest();

    // Callback function when XMLHttpRequest is ready
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState === 4){
            if (xmlhttp.status === 200) {
                document.getElementById('container').innerHTML = xmlhttp.responseText;

                // If onenter function specified
                if (onenter) {
                    onenter();
                }
            }
            else {
                document.getElementById('container').innerHTML = "Error loading page " + url;
            }
        }
    };
    xmlhttp.open("GET", url , true);
    xmlhttp.send();
}
</script>

 
Referring to the code snippet above, the PhoneGap library is loaded, since the application will use PhoneGap APIs. In particular, we will call the compass API in screen3.html.
 
Several JavaScript functions are used by screen3.html to access the compass API, so we include them by loading screen3.js.
 
We have modified loadPage() to include two additional parameters. The onleave parameter is a function that is called when leaving the current screen, and the onenter function is called after the new screen is loaded.
 
We add a new button to screen1 that loads screen3, and runs the deviceInfo() function necessary to initialize the screen3 contents. This is accomplished by setting the onenter parameter.
 
<h3>Screen1.html</h3>
This is screen1.html.<br>
<button type="button" onclick="loadPage('screen2.html')">Load screen 2</button>
<button type="button" onclick="loadPage('screen3.html', null, deviceInfo);">Load screen 3</button>

 
As shown below, screen3 calls functions defined in screen3.js to obtain the current compass heading and display it.
 
<h3>Screen3.html</h3>
This page calls PhoneGap for retrieving device information.
<div class="info">
    Platform: <span id="platform"> </span><br>
    Version: <span id="version"> </span><br>
    UUID: <span id="uuid"> </span><br>
</div>
<br>
You can press one of the buttons below to get the compass reading.
<div class="info">
    Heading: <span id="compassHeading"> </span><br>
</div>
<br>
<button type="button" onclick="getCompass();">Get Compass</button>
<button type="button" onclick="watchCompass();">Begin Watch Compass</button>
<button type="button" onclick="stopWatchCompass();">Stop Watch Compass</button>
<br>
<button type="button" onclick="loadPage('screen1.html');">Load screen 1</button>
<button type="button" onclick="loadPage('screen2.html');">Load screen 2</button>

 
There are several helper functions defined in screen3.js that invoke the compass APIs directly.
 
var deviceInfo = function(){
    document.getElementById("platform").innerHTML = device.platform;
    document.getElementById("version").innerHTML = device.version;
    document.getElementById("uuid").innerHTML = device.uuid;
}

var getCompass = function() {
    ...
}

var watchCompass = function() {
    ...
}

var stopWatchCompass = function() {
    ...
}

 
When screen3.html is passed to loadPage('screen3.html', null, deviceInfo), the deviceInfo() function is called immediately after the file is loaded. This ensures that the HTML elements defined in screen3.html exist before attempting to fill in the device information determined by PhoneGap.
 

Transitions between Screens


JavaScript toolkits such as Dojo have transitions or animations when navigating from one screen to another. This feature can be easily added to our example by using the css webkitTransition property. Instead of replacing the container div with our new screen, two divs can be used as HTML containers between which we animate.
 
<body id="body" onload="init();">
    <h3 id="header">PhoneGap Dynamic Pages Demo</h3>
    <div id="container">
        <div id="div0"></div>
        <div id="div1"></div>
    </div>
</body>

 
The following code slides both div0 and div1 left at the same rate, with div0 sliding off the screen to the left and div1 sliding onto the screen from the right.
 
// Slide div0 out and div1 in from left 
div1.innerHTML = content;
div0.style.webkitTransition = "left 0.5s ease-in";
div1.style.webkitTransition = "left 0.5s ease-in";
div0.style.left = "-100%";
div1.style.left = "0px";

// Clear transition so div0 can be moved to the right of div1
setTimeout(function() {
    div0.style.webkitTransition = null;
    div1.style.webkitTransition = null;
    div0.style.left = "100%";
 
}, 500);

 
Once the transition has completed after 0.5 seconds, we reposition div0 at left=100% so that it can be used by the next screen to slide in from the right. With the transitions specified above, all screens will slide from right to left. You can reverse this by specifying a different direction in the webkitTransition property.
 

Summary


This blog post showed how to write mobile applications that use dynamic page loading to load each screen of an app individually, instead of loading all screens at once inside a single HTML file. The result is a shorter startup time for large, complex mobile applications, and simplified development and management of individual screens.
 

Code


The source code for this blog post is available as a PhoneGap Android project at https://github.com/brycecurtis/articles/tree/master/DynamicPages.
 
The index.html file has links for two examples. The first one, simple.html, demonstrates how to do a simple replace of content in a container div. The second file, transition.html, demonstrates how to use two divs to animate the transition between screens.
 

Links



 

Comments