This is the last part of a three parts jQuery Mobile Tutorial. For introduction to the Tutorial please refer to Part I.

The Tutorial walks you through a process of building an application which will allow you to maintain a list of your favorite websites and share sites from the list with other mobile users through QR codes. In Part II we implemented functionality to display QR codes for sites from a static, hardcoded list. In this part we will add ability to maintain the list of sites.

3.1 List storage

Before we start adding and deleting list items we need to have a mechanism to store a list. We will use HTML5 local storage.

Let’s start by adding a function to retrieve a list from the local storage. It will also initialize the list using defaults if a list cannot be found in the local storage.


// Retrieve a list of URLs from the local storage.
// Use defaults if storage has not been initialized yet.
// URLs are serialized using JSON for storage.
function getMyUrls() {
var myUrls;
var storedUrls = localStorage.getItem("myUrls");
if (storedUrls) {
// Deserialize URLs
myUrls = JSON.parse(storedUrls);
}
else {
// Initialize defaults
myUrls = [encodeURIComponent("https://ctoinsights.wordpress.com"), encodeURIComponent("http://www.book-current.com")];
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
return myUrls;
}

view raw

shareqr3.1.1.js

hosted with ❤ by GitHub

You can of course use your preferred sites to initialize the list.

Now we can use the list to update cotent of the Home page:


// Display a list of urls you want to share.
function showUrlList(urlObj, options) {
// Get list of urls
var myUrls = getMyUrls();
// Get the page we are going to write our content into.
var $page = $("#home");
// Get the content area element for the page.
var $content = $page.children(":jqmData(role=content)");
// Build the list of urls.
var markup = "<ul data-role='listview'>";
for (var i=0; i<myUrls.length; i++) {
markup = markup + "<li><a href='#qrcode?url=" + myUrls[i] + "'>" + getHostname(myUrls[i]) + "</a></li>";
}
markup = markup + "</ul>";
// Inject the list markup into the content element.
$content.html(markup);
// Pages are lazily enhanced. We call page() on the page
// element to make sure it is always enhanced before we
// attempt to enhance the listview markup we just injected.
$page.page();
// Enhance the listview we just injected.
$content.find( ":jqmData(role=listview)" ).listview();
// Now call changePage() and tell it to switch to the page we just modified.
$.mobile.changePage($page, options);
}

view raw

shareqr3.1.2.js

hosted with ❤ by GitHub

Next we have to modify pagebeforechange event handler to catch requests to display the Home page in addition to the previously implemented code handling QR Code page display requests:


// Listen for any attempts to call changePage().
$(document).bind( "pagebeforechange", function(e, data) {
// We only want to handle changePage() calls where the caller is asking us to load a page by URL.
if (typeof data.toPage === "string") {
// We only want to handle a subset of URLs.
var u = $.mobile.path.parseUrl(data.toPage);
var home = /^#home/;
var qrcode = /^#qrcode/;
if (u.hash.search(home) !== -1) {
// Display a list of URLs.
showUrlList(u, data.options);
e.preventDefault();
}
else if (u.hash.search(qrcode) !== -1) {
// Display QR code for the selected URL.
showQRCode(u, data.options);
e.preventDefault();
}
}
});

view raw

shareqr3.1.3.js

hosted with ❤ by GitHub

Finally we have to make sure that the Home page is properly populated when loaded for the first time.


// Refresh home page using dynamic url list.
$(document).ready(function() {
$.mobile.changePage("#home");
});

view raw

shareqr3.1.4.js

hosted with ❤ by GitHub

This replicates functionality developed in Part II except that the site list is now pulled from the local storage instead of being hardcoded.

3.2 Adding sites

To be able to add a site, first we will create an additional page with a form to capture a site url.


<div data-role="page" id="addurl">
<div data-role="header" data-theme="b" data-position="fixed">
<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
<h1>Add URL</h1>
<a href="#home" data-icon="arrow-l" data-rel="back">Back</a>
</div>
<div data-role="content">
<form action="#home" method="POST" name="addurl" id="addurl">
<div data-role="fieldcontain">
<label for="url">URL:</label>
<input type="url" name="url" id="url" value="http://&quot; />
<button>Add</button>
</div>
</form>
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
<a href="#about" data-icon="info">About</a>
</div>
</div>

Next we will add a a link to the Add URL page on the Home page header.


<a href="#addurl" data-icon="plus">Add URL</a>

We will also have to capture the form submit event and save a newly entered url in the local storage.


// When a new url is added, save it in the local storage and display the home page.
$("#addurl").live("submit" , function(e, data) {
var url = $("#url").val();
addUrl(url);
$.mobile.changePage("#home");
return false;
});
// Add a URL to the list.
function addUrl(url) {
var myUrls = getMyUrls();
// Check for duplicates
if (findUrl(url) === -1) {
myUrls = myUrls.concat(encodeURIComponent(url));
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
}
// Find URL in the url list.
// Return index or -1 if not found.
function findUrl(url) {
var index = -1;
var myUrls = getMyUrls();
for (var i=0; i < myUrls.length; i++) {
if (myUrls[i] === encodeURIComponent(url)) {
return i;
}
}
return index;
}

view raw

shareqr3.2.3.js

hosted with ❤ by GitHub

3.3 Deleting sites

List management would not be complete without an ability to delete items from the list. We will start by adding Delete buttons to the list items on the Home page. Let’s modify the showUrlList function.


// Build the list of urls.
var markup = "<ul data-role='listview' data-split-icon='delete'>";
for (var i=0; i<myUrls.length; i++) {
markup = markup + "<li><a href='#qrcode?url=" + myUrls[i] + "'>" + getHostname(myUrls[i]) + "</a>" + "<a href='#delurl?url=" + myUrls[i] + "' data-rel='dialog'>Delete</a></li>";
}
markup = markup + "</ul>";

view raw

shareqr3.3.1.js

hosted with ❤ by GitHub

We are using a split button list. You can find out more about jQuery Mobile list capabilities here.

Clicking on a Delete button will open up a Delete URL dialog box. We need to add the dialog box to the html file.


<div data-role="page" id="delurl">
<div data-role="header" data-theme="b" data-position="fixed">
<h1>Delete URL?</h1>
</div>
<div data-role="content">
<p class="center" id="url_prompt">url</p>
<form action="#home" method="POST" name="delurl" id="delurl">
<div data-role="fieldcontain">
<input type="hidden" name="url_value" id="url_value" value="url" />
<fieldset class="ui-grid-a">
<div class="ui-block-a">
<a href="#home" data-role="button" class="ui-submit" data-rel="back" data-direction="reverse">Cancel</a>
</div>
<div class="ui-block-b">
<input type="submit" value="Delete" />
</div>
</fieldset>
</div>
</form>
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
</div>
</div>

Dialog box buttons are positioned using a layout grid.

The dialog box has to be dynamically populated with a site url. We need to modify pagebeforechange event handler to capture requests to display the dialog box.  We also need to write a function to update content of the dialog box.


// Listen for any attempts to call changePage().
$(document).bind( "pagebeforechange", function(e, data) {
// We only want to handle changePage() calls where the caller is
// asking us to load a page by URL.
if (typeof data.toPage === "string") {
// We only want to handle a subset of URLs.
var u = $.mobile.path.parseUrl(data.toPage);
var home = /^#home/;
var qrcode = /^#qrcode/;
var delurl = /^#delurl/;
if (u.hash.search(home) !== -1) {
// Display a list of URLs.
showUrlList(u, data.options);
e.preventDefault();
}
else if (u.hash.search(qrcode) !== -1) {
// Display QR code for the selected URL.
showQRCode(u, data.options);
e.preventDefault();
}
else if (u.hash.search(delurl) !== -1) {
// Display URL delete confirmation dialog box.
showDelUrl(u, data.options);
e.preventDefault();
}
}
});
// Display Delete URL confirmation dialog for a specific url passed in as a parameter.
function showDelUrl(urlObj, options) {
// Get the url parameter
var url = decodeURIComponent(urlObj.hash.replace(/.*url=/, ""));
// The pages we use to display our content are already in
// the DOM. The id of the page we are going to write our
// content into is specified in the hash before the '?'.
var pageSelector = urlObj.hash.replace(/\?.*$/, "");
// Get the page we are going to write our content into.
var $page = $(pageSelector);
// Get the content area element for the page.
var $content = $page.children(":jqmData(role=content)");
// Set url elements of the page.
$content.find("#url_value").val(url);
$content.find("#url_prompt").html(getHostname(url));
// Pages are lazily enhanced. We call page() on the page
// element to make sure it is always enhanced.
$page.page();
// Now call changePage() and tell it to switch to the page we just modified.
$.mobile.changePage($page, options);
}

view raw

shareqr3.3.3.js

hosted with ❤ by GitHub

Finally we need to capture the dialog box submit requests and remove a site from the local storage.


// When a url is deleted, remove it from the local storage and display the home page.
$("#delurl").live("submit" , function(e, data) {
var url = $("#url_value").val();
delUrl(url);
$.mobile.changePage("#home");
return false;
});
// Delete URL from the list.
function delUrl(url) {
var myUrls = getMyUrls();
var index = findUrl(url);
if (index !== -1) {
myUrls.splice(index, 1);
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
}

view raw

shareqr3.3.4.js

hosted with ❤ by GitHub

Complete source code is shown below:


<!DOCTYPE html>
<html>
<head>
<title>Share QR</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css&quot; />
<link rel="stylesheet" href="shareqr3.3.css" />
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script&gt;
<script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script&gt;
<script src="shareqr3.3.js"></script>
</head>
<body>
<div data-role="page" id="home">
<div data-role="header" data-theme="b" data-position="fixed">
<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
<h1>Share QR</h1>
<a href="#addurl" data-icon="plus">Add URL</a>
</div>
<div data-role="content">
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
<a href="#about" data-icon="info">About</a>
</div>
</div>
<div data-role="page" id="qrcode">
<div data-role="header" data-theme="b" data-position="fixed">
<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
<h1>QR Code</h1>
<a href="#home" data-icon="arrow-l" data-rel="back">Back</a>
</div>
<div data-role="content">
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
<a href="#about" data-icon="info">About</a>
</div>
</div>
<div data-role="page" id="addurl">
<div data-role="header" data-theme="b" data-position="fixed">
<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
<h1>Add URL</h1>
<a href="#home" data-icon="arrow-l" data-rel="back">Back</a>
</div>
<div data-role="content">
<form action="#home" method="POST" name="addurl" id="addurl">
<div data-role="fieldcontain">
<label for="url">URL:</label>
<input type="url" name="url" id="url" value="http://&quot; />
<button>Add</button>
</div>
</form>
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
<a href="#about" data-icon="info">About</a>
</div>
</div>
<div data-role="page" id="delurl">
<div data-role="header" data-theme="b" data-position="fixed">
<h1>Delete URL?</h1>
</div>
<div data-role="content">
<p class="center" id="url_prompt">url</p>
<form action="#home" method="POST" name="delurl" id="delurl">
<div data-role="fieldcontain">
<input type="hidden" name="url_value" id="url_value" value="url" />
<fieldset class="ui-grid-a">
<div class="ui-block-a">
<a href="#home" data-role="button" class="ui-submit" data-rel="back" data-direction="reverse">Cancel</a>
</div>
<div class="ui-block-b">
<input type="submit" value="Delete" />
</div>
</fieldset>
</div>
</form>
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
</div>
</div>
<div data-role="page" id="about">
<div data-role="header" data-theme="b" data-position="fixed">
<a href="#home" data-icon="home" data-iconpos="notext">Home</a>
<h1>About</h1>
<a href="#home" data-icon="arrow-l" data-rel="back">Back</a>
</div>
<div data-role="content">
<p>Share your favorite URLs with other mobile phone users through QR codes.</p>
</div>
<div data-role="footer" class="ui-bar" data-theme="b" data-position="fixed" data-id="footer">
<a href="#about" data-icon="info">About</a>
</div>
</div>
</body>
</html>

view raw

shareqr3.3.html

hosted with ❤ by GitHub


// Refresh home page using dynamic url list.
$(document).ready(function() {
$.mobile.changePage("#home");
});
// Listen for any attempts to call changePage().
$(document).bind( "pagebeforechange", function(e, data) {
// We only want to handle changePage() calls where the caller is
// asking us to load a page by URL.
if (typeof data.toPage === "string") {
// We only want to handle a subset of URLs.
var u = $.mobile.path.parseUrl(data.toPage);
var home = /^#home/;
var qrcode = /^#qrcode/;
var delurl = /^#delurl/;
if (u.hash.search(home) !== -1) {
// Display a list of URLs.
showUrlList(u, data.options);
e.preventDefault();
}
else if (u.hash.search(qrcode) !== -1) {
// Display QR code for the selected URL.
showQRCode(u, data.options);
e.preventDefault();
}
else if (u.hash.search(delurl) !== -1) {
// Display URL delete confirmation dialog box.
showDelUrl(u, data.options);
e.preventDefault();
}
}
});
// When a new url is added, save it in the local storage and display the home page.
$("#addurl").live("submit" , function(e, data) {
var url = $("#url").val();
addUrl(url);
$.mobile.changePage("#home");
return false;
});
// When a url is deleted, remove it from the local storage and display the home page.
$("#delurl").live("submit" , function(e, data) {
var url = $("#url_value").val();
delUrl(url);
$.mobile.changePage("#home");
return false;
});
// Display a list of urls you want to share.
function showUrlList(urlObj, options) {
// Get list of urls
var myUrls = getMyUrls();
// Get the page we are going to write our content into.
var $page = $("#home");
// Get the content area element for the page.
var $content = $page.children(":jqmData(role=content)");
// Build the list of urls.
var markup = "<ul data-role='listview' data-split-icon='delete'>";
for (var i=0; i<myUrls.length; i++) {
markup = markup + "<li><a href='#qrcode?url=" + myUrls[i] + "'>" + getHostname(myUrls[i]) + "</a>" + "<a href='#delurl?url=" + myUrls[i] + "' data-rel='dialog'>Delete</a></li>";
}
markup = markup + "</ul>";
// Inject the list markup into the content element.
$content.html(markup);
// Pages are lazily enhanced. We call page() on the page
// element to make sure it is always enhanced before we
// attempt to enhance the listview markup we just injected.
$page.page();
// Enhance the listview we just injected.
$content.find( ":jqmData(role=listview)" ).listview();
// Now call changePage() and tell it to switch to the page we just modified.
$.mobile.changePage($page, options);
}
// Load the QR Code for a specific url passed in as a parameter.
// Generate markup for the page, and then make that page the current active page.
function showQRCode(urlObj, options) {
// Get the url parameter
var qrUrl = decodeURIComponent(urlObj.hash.replace(/.*url=/, ""));
// The page we use to display QR code is already in the DOM.
// The id of the page we are going to write the content into is specified in the hash before the '?'.
var pageSelector = urlObj.hash.replace(/\?.*$/, "");
if (qrUrl) {
// Get the page we are going to write content into.
var $page = $(pageSelector);
// Get the header for the page.
var $header = $page.children(":jqmData(role=header)");
// Find the h1 element in the header and inject the hostname from the url.
$header.find("h1").html(getHostname(qrUrl));
// Get the content area element for the page.
var $content = $page.children(":jqmData(role=content)");
// The markup we are going to inject into the content area of the page.
var markup = "<img class='center' src=https://chart.googleapis.com/chart?chs=200×200&cht=qr&chl=" + qrUrl + " alt=" + qrUrl + " />";
// Inject the QR code markup into the content element.
$content.html(markup);
// Make sure the url displayed in the the browser's location field includes parameters
options.dataUrl = urlObj.href;
// Now call changePage() and tell it to switch to the page we just modified.
$.mobile.changePage($page, options);
}
}
// Display Delete URL confirmation dialog for a specific url passed in as a parameter.
function showDelUrl(urlObj, options) {
// Get the url parameter
var url = decodeURIComponent(urlObj.hash.replace(/.*url=/, ""));
// The pages we use to display our content are already in
// the DOM. The id of the page we are going to write our
// content into is specified in the hash before the '?'.
var pageSelector = urlObj.hash.replace(/\?.*$/, "");
// Get the page we are going to write our content into.
var $page = $(pageSelector);
// Get the content area element for the page.
var $content = $page.children(":jqmData(role=content)");
// Set url elements of the page.
$content.find("#url_value").val(url);
$content.find("#url_prompt").html(getHostname(url));
// Pages are lazily enhanced. We call page() on the page
// element to make sure it is always enhanced.
$page.page();
// Now call changePage() and tell it to switch to the page we just modified.
$.mobile.changePage($page, options);
}
// Extract hostname from a url.
function getHostname(url) {
return decodeURIComponent(url).replace(/.*\/\//, "").replace(/\/.*$/, "");
}
// Retrieve a list of URLs from the local storage.
// Use defaults if storage has not been initialized yet.
// URLs are serialized using JSON for storage.
function getMyUrls() {
var myUrls;
var storedUrls = localStorage.getItem("myUrls");
if (storedUrls) {
// Deserialize URLs
myUrls = JSON.parse(storedUrls);
}
else {
// Initialize defaults
myUrls = [encodeURIComponent("https://ctoinsights.wordpress.com&quot;), encodeURIComponent("http://www.book-current.com&quot;)];
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
return myUrls;
}
// Find URL in the url list.
// Return index or -1 if not found.
function findUrl(url) {
var index = -1;
var myUrls = getMyUrls();
for (var i=0; i < myUrls.length; i++) {
if (myUrls[i] === encodeURIComponent(url)) {
return i;
}
}
return index;
}
// Add a URL to the list.
function addUrl(url) {
var myUrls = getMyUrls();
// Check for duplicates
if (findUrl(url) === -1) {
myUrls = myUrls.concat(encodeURIComponent(url));
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
}
// Delete URL from the list.
function delUrl(url) {
var myUrls = getMyUrls();
var index = findUrl(url);
if (index !== -1) {
myUrls.splice(index, 1);
localStorage.setItem("myUrls", JSON.stringify(myUrls));
}
}

view raw

shareqr3.3.js

hosted with ❤ by GitHub

We also made a small addition to the css file to center display of a url, and make look of Cancel and Delete button consistent in the Delete URL dialog box.


p.center {
text-align: center;
}
img.center {
display: block;
margin-left: auto;
margin-right: auto;
}
#delurl fieldset {
text-align: center;
}
#delurl .ui-btn.ui-submit {
margin: 0 5px;
}

view raw

shareqr3.3.css

hosted with ❤ by GitHub

This completes the Tutorial. We built a simple but fully functional web application utilizing many features of jQuery Mobile framework. You can access the application on your mobile phone at http://rtekie.herokuapp.com/shareqr3.3.html. Sample screen shots are shown below:

You can find more tutorials and other resources at http://jquerymobile.com/resources/.
What do you think about this tutorial?