Any regular readers may have noticed that I finally got around to updating and adding a new book under "My Books" on the right-hand side of the screen. Yes, I not-so recently published my third book (and second with O'Reilly) earlier this year.
The book is called 20 Recipes for Programming PhoneGap Cross-Platform Mobile Development for Android and iPhone. Throughout this book, I get you up and running quickly using the PhoneGap API along with the Jquery Mobile API to take care of the pesky mobile design issues.
Much like I did when I released my MVC 3.NET book, I provided a free chapter from the book for my readers. Well here is an excellent recipe sample from my PhoneGap book. The goal of this recipe is to provide a standard approach to automatically fire (or trigger) a JavaScript function when a PhoneGap page has loaded either on initial launch or after the user has navigated within the application.
Executing a Callback Function Once the Device Is Ready
Problem
After a page has loaded, you will want to execute some JavaScript code immediately without invoking it manually each time in your JavaScript code.
Solution
Because HTML doesnt allow for a lot of dynamic features, a lot of code needs to be duplicated. To minimize the page load times, you should load the minimal amount of content each time while reusing as much code as you can. The common.js JavaScript file must be kept lightweight, and additional JavaScript files should be created for other new functionality. However, there currently is no process to allow for additional function calls once the application has determined that the device is ready.
You can update the common.js JavaScript file with an automatic callback function that will be executed once the common code to detect the device type, network connection, etc., has finished working. This will enable you to use the same process in many future recipes.
Discussion
Because the application is using jQuery mobile, you must reorganize some of the existing code in order to improve code loading. When you navigate between pages using jQuery mobile, it performs the request via AJAX and strips all of the content from the HTML file (unless it is within a div tag that contains a data-role of type page). This Executing a Callback Function Once the Device Is Ready means that the previous window.onload event will no longer trigger. Instead, a new event that is provided by the jQuery mobile library will be used.
The first thing to note is that the index.html page from Creating a Persistent Navigation System on page 5 requires some reformatting as follows:
<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap</title>
<link rel="stylesheet" href="css/jquery.mobile-1.0rc1.min.css" />
<script type="text/javascript" charset="utf-8" src="scripts/phonegap-1.0.0.js"></script>
</head>
<body>
<div data-role="page" id="index-page">
<h1>Hello World!</h1>
<div data-role="footer" data-position="fixed">
<div data-role="navbar">
<ul>
<li>
<a href="index.html">Home</a>
</li>
<li>
<a href="about.html">About</a>
</li>
</ul>
</div>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="scripts/jquery-1.6.4.min.js"></script>
<script type="text/javascript" charset="utf-8" src="scripts/jquery.mobile-1.0rc1.min.js"></script>
<script type="text/javascript" charset="utf-8" src="scripts/common.js"></script>
</body>
</html>
Several things have changed in this code. Most of the JavaScript files have been moved out of the head tag. Ive left the main PhoneGap file there to ensure that it loads completely before the page does, because anything placed inside the head tag must fully finish loading before you continue. Next, a new div tag was added with a data-role of type page. Finally, the previous JavaScript files have been moved to the bottom and the order has been altered. These have also been placed outside of the page div tag, because they do not need to be loaded again if the user navigates back to the index page. The order of the files was altered because, in the next example, the common.js file will be updated to use elements of jQuery and the mobile library, which must load first.
Within the next example is an updated common.js file. It contains all of the code from the first several recipes that performs the following operations: device ready, device detection, and network detection, as well as the new callback feature. The key objective of this expanded code is to allow you to run custom code tied to the name of a particular page, when that page loads.
var isPhoneGapReady = false;
var isAndroid = false;
var isBlackberry = false;
var isIphone = false;
var isWindows = false;
var deviceUUID;
var isConnected = false;
var isHighSpeed;
var internetInterval;
var currentUrl;
function init(url) {
if (typeof url != 'string') {
currentUrl = location.href;
} else {
currentUrl = url;
}
if (isPhoneGapReady) {
onDeviceReady();
} else {
document.addEventListener("deviceready", onDeviceReady, false);
}
}
function onDeviceReady() {
isPhoneGapReady = true;
deviceUUID = device.uuid;
deviceDetection();
networkDetection();
executeEvents();
executeCallback();
}
function executeEvents() {
if (isPhoneGapReady) {
document.addEventListener("online", onOnline, false);
document.addEventListener("offline", onOffline, false);
internetInterval = window.setInterval(function() {
if (navigator.network.connection.type != Connection.NONE) {
onOnline();
} else {
onOffline();
}
}, 5000);
}
}
function executeCallback() {
if (isPhoneGapReady) {
var pages = currentUrl.split("/");
var currentPage = pages[pages.length - 1].slice(0, pages[pages.length - 1].indexOf(".html"));
currentPage = currentPage.charAt(0).toUpperCase() + currentPage.slice(1);
if (typeof window['on' + currentPage + 'Load'] == 'function') {
window['on' + currentPage + 'Load']();
}
}
}
function deviceDetection() {
if (isPhoneGapReady) {
switch (device.platform) {
case "Android":
isAndroid = true;
break;
case "Blackberry":
isBlackberry = true;
break;
case "iPhone":
isIphone = true;
break;
case "WinCE":
isWindows = true;
break;
}
}
}
function networkDetection() {
if (isPhoneGapReady) {
if (navigator.network.connection.type != Connection.NONE) {
isConnected = true;
}
switch (navigator.network.connection.type) {
case Connection.UNKNOWN:
case Connection.CELL_2G:
isHighSpeed = false;
break;
default:
isHighSpeed = true;
break;
}
}
}
function onOnline() {
isConnected = true;
}
function onOffline() {
isConnected = false;
}
$(document).bind("pageload", function(event, data) {
init(data.url);
});
window.onload = init;
There is quite a bit happening in the preceding code. I will start at the bottom with the two events that call the init function. The window.onload code remains as-is and will be called when the application first loads. By binding the pageload event to the document, I ensure that each time a user clicks a new link, this event will fire when that page has finished loading. It is also passing the current URL to the updated init function.
This will be used for implementing the callback function. The init function has been updated to accept this new url parameter. However, since this parameter is not passed in by the window.onload event, the code checks to see whether it is a string. When a string is not detected (i.e., on first load), the location.href is used and stored in the currentUrl global variable. Then, if the variable isPhoneGapReady is already set and true, there is no need to add the listener and wait, so it just calls the onDeviceReady function.
The onDeviceReady function has been slightly reorganized and some of the previous work has been moved into new functions for later expansion, including the newly added executeCallback function.
The executeCallback function takes the currentUrl variable and splits it into parts to be able to retrieve just the filename, e.g., the index. This name is then used to check whether there is a function called onIndexLoad. If this function exists, it is executed.
When you add future pages, you can also add new functions that will be executed automatically once the page loads. These will perform any additional processing required by that page. For instance, if you add an onAboutLoad function, the app will execute it when about.html has finished loading.