jQuery Mobile is a relatively new mobile web framework, with its first release made in October 2010. The framework has many interesting features to support development of web based mobile applications. In this tutorial, we will focus on some basic elements of jQuery Mobile: page structure, forms and Ajax form submission. The tutorial is based on version 1.0 alpha release 2 of the jQuery Mobile framework.
As part of the tutorial we will write a small B2C application. A package shipment company has a form in their web site for customers to enter information on packages that are lost or damaged during shipment. Using a hand-held device (e.g. a web enabled phone) a customer inputs information to the form about their claim: the shipment number from the original receipt, name/address, and a description of loss/damage. When the customer submits the form, the web site responds with a claim id for further tracking. The scenario is shown in the diagram below:
jQuery Mobile framework supports a variety of browsers including iOS devices, Android devices, Blackberry OS 6, and webOS (for a support matrix, see http://jquerymobile.com/gbs/). The application in this tutorial has been tested against an Android 2.2 device and an iOS 4.1 device.
Design Considerations
Before going into technical details, let us talk about main considerations driving the design of the application.
- Short response time: It is preferred for the web application to contain a single html page with a simple user interface. All user interface constructs, such as error dialog and confirmation pages, will be part of the html page. Once the page is downloaded into the browser, different sections of the page will be shown to the user depending on the particular context. There will be no need to open multiple network connections to download pages several times.
- Error handling: If the user forgets to enter a required field in the form, submission should fail with a warning dialog. Missing fields should be easy for the user to find.
- Support for multiple devices/browsers: Applications should have consistent look-and-feel and behavior across supported devices and browsers.
Of course, a typical real-life application will have additional or different design concerns. For the purposes of this tutorial, our scope will be limited to the considerations above.
jQuery Mobile Introduction
Most of the discussion in this section has been borrowed from the documentation in http://jquerymobile.com. See the original reference for further details.
jQuery Mobile is a user interface system built on the popular JavaScript framework jQuery. It consists of user interface elements and programming constructs that provide consistent functionality across a large variety of mobile and desktop web browsers. The 'Progressive Enhancement' and 'Graceful Degradation' principles are behind its design. Core functionality of the framework supports a broad set of platforms whereas newer platforms benefit from more enhanced features.
jQuery Mobile has a relatively small footprint. Since basic configuration of a jQuery Mobile page is done purely by markup, it is possible to achieve quick development cycles, particularly if your application does not need complex programming functions. Although built on jQuery core, theming system of jQuery Mobile is based on a new CSS framework that aims to separate color and texture from structural styles that define things like padding and dimensions. An event handling API handles touch, mouse, and cursor focus-based user input methods in a unified manner.
jQuery Mobile comes out of the box with many user interface elements such as header and footer toolbars, buttons with icons, form elements (including touch sensitive slider and toggle switches) and lists. Basic HTML styles, two or three column grids, and collapsible elements are also provided.
Inclusion of jQuery Mobile Libraries
As of jQuery Mobile 1.0 alpha 2 release, the following stylesheet and JavaScript libraries must be included in your HTML pages. You can reference them as below or serve them from your own server:
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css" /> <script src="http://code.jquery.com/jquery-1.4.4.min.js"></script> <script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script>
Container and Content Pages
Let us introduce the basic structure of a page in jQuery Mobile. A page in jQuery Mobile framework can be a single page or a local internal linked 'page' within a page. A 'container' page will include one or more 'content' pages. The boundary of a container page is defined as follows:
<div data-role="page"> ... </div>
Observe that the value of the data-role
attribute is page
. On the other hand, the boundary of a content page is defined as follows:
<div data-role="content"> ... </div>
Observe that the value of the data-role
attribute is content
.
A content page may have an associated header and footer. As an example, a container page with several content pages may have the following structure:
<div data-role="page"> <div data-role="header">...</div> <div id="contentWithHeaderAndFooter1" data-role="content">...</div> <div data-role="footer">...</div> <div data-role="header">...</div> <div id="contentWithHeaderAndFooter2" data-role="content">...</div> <div data-role="footer">...</div> <div id="contentWithNoHeaderAndFooter" data-role="content">...</div> </div>
When a container page is loaded, all content pages in it will be visible. In our application, we need to display only one content at a time. Therefore, we need to programmatically control which content will be displayed depending on context.
Hiding/Showing Content Pages
In order to hide an element, use the jQuery Mobile API hide()
function:
$('#hdrMain').hide();
will hide the header with id
hdrMain
. Here, we used the jQuery ID selector by passing id
of the element we would like to select preceded by #
sign. Conversely, the show()
function shows a hidden element:
$('#hdrMain').show();
The hide()
and show()
functions apply to elements of many different types, in particular, <div>
tags and anchors (<a>
tags). In this tutorial, we will use hide()
and show()
functions extensively to display only the relevant context to the application user. More details are given below.
Step 1: Demo Application Page Structure
Our demo application consists of a single html page, index.html
, which consists of a container page as shown below:
<div data-role="page" data-theme="b" id="page1"> <!-- ====== main content starts here ===== --> <div data-role="header" id="hdrMain" name="hdrMain" data-nobackbtn="true">...</div> <div data-role="content" id="contentMain" name="contentMain"> ... </div> <div data-role="footer" id="ftrMain" name="ftrMain"></div> <!-- ====== main content ends here ===== --> <!-- ====== dialog content starts here ===== --> <div align="CENTER" data-role="content" id="contentDialog" name="contentDialog"> ... </div> <!-- ====== dialog content ends here ===== --> <!-- ====== transition content page starts here ===== --> <div data-role="content" id="contentTransition" name="contentTransition"> ... </div> <!-- ====== transition content ends here ===== --> <!-- ====== confirmation content starts here ===== --> <div data-role="header" id="hdrConfirmation" name="hdrConfirmation" data-nobackbtn="true">...</div> <div data-role="content" id="contentConfirmation" name="contentConfirmation" align="center"> ... </div> <div data-role="footer" id="ftrConfirmation" name="ftrConfirmation"></div> <!-- ====== confirmation content ends here ===== --> ... </div><!-- page1 -->
- The main content contains the form to be filled out by the user and has both a header and a footer.
- Dialog content is displayed only if a required form field is missing when form is submitted. It consists of a warning and an OK button to close the dialog. Observe that it does not have a header or footer.
- Transition content is displayed after the form is submitted until the response is received from the server. It consists of a "spinner" image with short text informing the user that their form is being submitted. Transition content also does not have a header or footer.
- Confirmation content is displayed a after response is received from the server. It displays the confirmation number to the user. Confirmation content has both a header and a footer.
Content transitions are shown in the diagram below:
Additional observations on the code listing above:
- The
data-theme
attribute allows us to choose from available styles in the jQuery Mobile framework:<div data-role="page" data-theme="b" id="page1">
. The default theme has various color swatches nameda, b, c, d, e,
each providing a consistent set of colors for different elements in the page. For our application we chose the color corresponding tob
. - Headers come with a back button. We did not need back buttons and therefore chose to hide them via
data-nobackbtn="true"
. - It is possible to provide some text to be displayed in the footer between the
<div>
tags. In our application we omitted text in the footer. As a result, the user will see in the footer only a thin bar colored the same as the header. With text, the footer will have a similar height as the footer with the text in it.
Form Elements
Our claims form consists of the following fields:
- Several
input
fields of type text: Shipping number, First name, Last name, Email, Phone, Street address, City and Postal code. All are required fields except Phone. - A
select
element for State. This is a required field. - A
textarea
element for a user to enter information regarding the missing or damaged shipment. This is a required field.
As an example, let us look at the text field for Shipping number:
<div id="shipDiv" data-role="fieldcontain"> <label for="shipNo">Shipping number*</label> <input id="shipNo" name="shipNo_r" type="text" /> </div>
We used a label
with a for
attribute whose value is the same as the id
of the input
element the label
is attached to. Furthermore, we wrapped the label
and input
in a div
with data-role
attribute valued as fieldcontain
. jQuery Mobile framework will utilize the fieldcontain
attribute value for special styling. In addition, look at name="shipNo_r"
. For this particular application, we decided to define the value of the name
attribute of any required form element to be the value of the id
attribute appended by _r. If the element is not required, the value of the name
attribute will be the same as the value of the id
attribute. For example, since Phone is not a required field, it is defined as follows:
<div id="phoneDiv" data-role="fieldcontain"> <label for="phone">Phone</label> <input id="phone" name="phone" type="text"/> </div>
As we will see later on, this special naming convention will help us detect missing fields during form submission.
Another notable form element is the select
element. Similar to the above, the value of the for
attribute is the same as the id of the select
element the label
is attached to. Also, the label
and select
are wrapped in a div
with the data-role
attribute value as the fieldcontain
. With the long list of options that we have in this application, jQuery Mobile framework will open the list in a new page when the user focuses on the select
element.
<div id="stateDiv" data-role="fieldcontain"> <label id="stateLabel" for="state">State*</label> <select id="state" name="state_r" tabindex="2"> <option value="ZZ">Please select a state</option> <option value="AL">Alabama</option> <option value="AK">Alaska</option> <option value="AZ">Arizona</option> ... </select> </div>
The form page as displayed in an Android 2.2 phone is shown below. The user will scroll through the page to access the elements in the form:
The same form is shown in an iPod touch running iOS 4.1:
Step 2: Variable Definitions
We will reference various elements in the html page throughout our code. It makes sense to define those references only once and reuse them as needed. For this reason, we declare globally used variables as well as constants in the head
section of the page:
<script> // Global declarations - assignments made in $(document).ready() below var hdrMainvar = null; var contentMainVar = null; var ftrMainVar = null; var contentTransitionVar = null; var stateLabelVar = null; var whatLabelVar = null; var stateVar = null; var whatVar = null; var form1var = null; var confirmationVar = null; var contentDialogVar = null; var hdrConfirmationVar = null; var contentConfirmationVar = null; var ftrConfirmationVar = null; var inputMapVar = null; // Constants var MISSING = "missing"; var EMPTY = ""; var NO_STATE = "ZZ"; </script>
Assignments of those variables are done in jQuery $(document).ready()
function using ID selectors, as shown below. (Recall that jQuery $(document).ready()
function is called when the entire DOM hierarchy in the page is loaded. That function is the best location to initialize our variables.) We also define inputMapVar
as a collection consisting of all input
elements with _r
in their name
attribute (The function call $('input[name*="_r"]')
selects all such elements).
<script> $(document).ready(function() { // Assign global variables hdrMainVar = $('#hdrMain'); contentMainVar = $('#contentMain'); ftrMainVar = $('#ftrMain'); contentTransitionVar = $('#contentTransition'); stateLabelVar = $('#stateLabel'); whatLabelVar = $('#whatLabel'); stateVar = $('#state'); whatVar = $('#what'); form1Var = $('#form1'); confirmationVar = $('#confirmation'); contentDialogVar = $('#contentDialog'); hdrConfirmationVar = $('#hdrConfirmation'); contentConfirmationVar = $('#contentConfirmation'); ftrConfirmationVar = $('#ftrConfirmation'); inputMapVar = $('input[name*="_r"]'); ... }); ... </script>
Step 3: Content Selection Functions
Let us now look at the content selection functions that will be called by event handlers.
For hiding and displaying the Main content and its header/footer, we use the hideMain()
and showMain()
functions:
function hideMain(){ hdrMainVar.hide(); contentMainVar.hide(); ftrMainVar.hide(); } function showMain(){ hdrMainVar.show(); contentMainVar.show(); ftrMainVar.show(); }
For hiding and displaying the transition content, we use the hideContentTransition()
and showContentTransition()
functions:
function hideContentTransition(){ contentTransitionVar.hide(); } function showContentTransition(){ contentTransitionVar.show(); }
For hiding and displaying the Dialog content, we use the hideContentDialog()
and showContentDialog()
functions:
function hideContentDialog(){ contentDialogVar.hide(); } function showContentDialog(){ contentDialogVar.show(); }
Finally, for hiding and displaying the confirmation content and its header/footer, we use the hideConfirmation()
and showConfirmation()
functions:
function hideConfirmation(){ hdrConfirmationVar.hide(); contentConfirmationVar.hide(); ftrConfirmationVar.hide(); } function showConfirmation(){ hdrConfirmationVar.show(); contentConfirmationVar.show(); ftrConfirmationVar.show(); }
When the page is loaded, only the main content should be displayed. For this reason, other content pages are hidden:
<script> $(document).ready(function() { // Assign global variables ... hideContentDialog(); hideContentTransition(); hideConfirmation(); }); ... </script>
We will discuss how to tie together those content selection functions with the event handlers below.
Step 4: Form Submission
When a user presses the submit button we need to validate the form data before actually submitting the form. To keep things simple, we will only check if all the required fields have been provided. If form validation is successful, we display the transition content which consists of a spinner image with a short text informing the user that their form is being submitted. If validation fails, we display the dialog content which consists of a warning and an OK button to close the dialog.
Form Validation
Labels of the form elements with missing data will be highlighted in red. For this purpose, we add a new style class named missing and extending the jQuery Mobile label
class.
label.missing { color:#FF0000; font-weight:bold; }
Below is the event handler for form submission. In typical jQuery notation, the identifier for form1
is passed to the jQuery object call to handle the submit event:
$('#form1').submit(function() { var err = false; // Hide the Main content hideMain(); // Reset the previously highlighted form elements stateLabelVar.removeClass(MISSING); whatLabelVar.removeClass(MISSING); inputMapVar.each(function(index){ $(this).prev().removeClass(MISSING); }); // Perform form validation inputMapVar.each(function(index){ if($(this).val()==null || $(this).val()==EMPTY){ $(this).prev().addClass(MISSING); err = true; } }); if(stateVar.val()==NO_STATE){ stateLabelVar.addClass(MISSING); err = true; } if(whatVar.val()==null||whatVar.val()==EMPTY){ whatLabelVar.addClass(MISSING); err = true; } // If validation fails, show Dialog content if(err == true){ showContentDialog(); return false; } // If validation passes, show Transition content showContentTransition(); // Submit the form $.post("/forms/requestProcessor.php", form1Var.serialize(), function(data){ confirmationVar.text(data); hideContentTransition(); showConfirmation(); }); return false; });
We set an error flag to false
and hide the main content that contains the form. We then reset the previously highlighted labels on each member of the collection inputMapVar
. To do that, we pass an inline function as an argument to each()
that simply contains $(this).prev().removeClass(MISSING);
. Note that this
is the selected input
element and prev()
returns its immediate previous sibling which is the label
associated with it.
The state
for state selection and what
for claim description are not text fields. Therefore, the corresponding labels have their styles reset separately.
After all the previously highlighted labels are reset we revisit the required input
elements to check if any of them has a missing value. If that is the case we add the missing class to the label associated with the input field:
// Perform form validation inputMapVar.each(function(index){ if($(this).val()==null || $(this).val()==EMPTY){ $(this).prev().addClass(MISSING); err = true; } }); if(stateVar.val()==NO_STATE){ stateLabelVar.addClass(MISSING); err = true; } if(whatVar.val()==null||whatVar.val()==EMPTY){ whatLabelVar.addClass(MISSING); err = true; }
In addition, the error flag is set to true and the error dialog is shown. The figure below shows the error dialog in our Android 2.2 phone:
After the user presses the OK button, the user is displayed the form page with the missing fields highlighted, as shown below. In that picture, the orientation of phone screen is horizontal. Observe that each label and its sibling text field are displayed in a single line as opposed to the vertical orientation in Figure 3 where the label is above the text field.
On the other hand, if validation is successful we show the transition content and submit the form as discussed below.
Submitting the Form via Ajax
The form submission uses the jQuery Mobile post
function that accepts three arguments:
$.post("/forms/requestProcessor.php", form1Var.serialize(), function(data){...});
- The first argument is the server URL to submit the form to.
- The second one is the form content to submit. To get the form content, we simply call
serialize()
on the jQuery object by passing it the identifier for our form. - The third argument is an in-line function to process the response,
data
, sent back from the server.
Note that the post
function performs an Ajax call which is asynchronous by nature. Immediately after calling the post
, program execution will continue by returning false from the submit
function and user will start seeing the Transition content.
The in-line function to process the response is executed only when the server returns its response. For simplicity, the server application processing the form data, requestProcessor.php
, returns a hard-coded claim id for the user to use for future reference. Before sending the claim id requestProcessor.php
, it sleeps 4 seconds to emulate server processing time. It is in this period that the application will display the Transition content.
<?php usleep(4000000); echo('FTREIK12345678'); ?>
When the server response is received, the event handler code is executed. The first step is to populate the span
tag named confirmation
with the value returned from the server. This is simply done with:
confirmationVar.text(data);
Then, we hide the Transition content and show the Confirmation content that contains the span
tag named confirmation
:
<div data-role="content" id="contentConfirmation" name="contentConfirmation" align="center"> <p>A new claim has been created based on data you have submitted.</p> <p>Your confirmation number is: <span id="confirmation" name="confirmation"></span> </p> </div>
The confirmation page displayed in our Android 2.2 phone is shown below (phone's orientation is horizontal):
The same confirmation page is displayed on an iPod touch as follows (orientation is vertical):
Deploying the Source Code
The source code for the tutorial has a simple folder structure. All resources are stored in a folder named forms
. In that folder, there are two sub-folders, css
and img
. The css
folder contains colors.css
, which has the label.missing
class, and img
stores wait.gif
, the spinner image. The index.html
and requestProcessor.php
files are directly under the forms
folder. In our testing, we used an Apache web server with version 2.2.11 running PHP version 5.3.0. If you place the forms
folder directly under the DocumentRoot
of the web server, you can access the application via http://[your_host_name]/folder/index.html.
Conclusion
In this tutorial we introduced basic concepts from jQuery Mobile framework with focus on page structure, basic styling, form elements and Ajax form submission. A sample claims processing application was introduced to demonstrate those concepts. We provided screenshots of various pages of the application executing in an Android 2.2 phone and an iPod touch device with iOS 4.1. Some observations and closing remarks are given below:
- Since jQuery Mobile is built on jQuery core, those who have prior knowledge of the jQuery framework could get up to speed with the jQuery Mobile framework easily.
- The ability of the framework to represent multiple content pages in a single html page with built-in functions to show/hide those content pages appeared very convenient to create a form application with different views depending on state. For example, this technique can be applied to implement 'wizard' style user guides or multi-page survey forms.
- Tested on desktop, Android 2.2, and iOS 4.1 platforms, both the look-and-feel and functional aspects of the sample application were consistent. During development, it should be possible to quickly test and debug the main aspects of a jQuery Mobile application in a desktop platform. Then, more testing can be done on the individual devices and browsers that the application is expected to support.
Comments