Overview
If you are loading your mobile application from the home screen you probably noticed that as soon as you minimize the application Safari Mobile will not keep track of the last visited page. That means that regardless of the page that you were looking at, the next time you switch back to your application, the browser will reload your application and start over again from the initial page.
The issue is that even if you minimize the application for just a second, the user will have to navigate –again, to the point where he or she left the experience.
Set/Get Cookies
A possible solution to this problem is to store the ID or URL of the last visited page inside a browser cookie and check for that value every time that application is initialized. In addition to the last visited page ID/URL you also need to decide for how long you will be tracking this information. If your users spend, for example, 5 min average time visiting your application, you may not want to restore the last visited page if the user comes back a week after his or her last visit. Cookies are particularly handy because you can assign an expiration date that will specify for how long the information will be kept.
Code for saving, editing, and deleting cookies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | var app = {}; app.cookie = { /* set cookies */ setCookie: function(name, value, exp, inMinutes) { var exp = exp == null? 7 : exp; var exdate = new Date(); if(inMinutes != null && inMinutes == true){ exdate.setMinutes(exdate.getMinutes() + exp); } else { exdate.setDate(exdate.getDate() + exp); } var cookieValue = escape(value) + ((exp == null) ? "" : "; expires="+exdate.toUTCString()); document.cookie = name + "=" + cookieValue; }, /* retrieve the value of a cookie */ getCookie: function(name) { var i,x,y,ARRcookies = document.cookie.split(";"); for (i=0;i<ARRcookies.length;i++){ x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")); y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1); x = x.replace(/^\s+|\s+$/g,""); if (x==name){ var value = unescape(y); return value; } } return null; }, /* delete cookies (set expiration time in the past) */ deleleCookie: function() { this.setCookie(name, '', -365); } }; |
var app = {}; app.cookie = { /* set cookies */ setCookie: function(name, value, exp, inMinutes) { var exp = exp == null? 7 : exp; var exdate = new Date(); if(inMinutes != null && inMinutes == true){ exdate.setMinutes(exdate.getMinutes() + exp); } else { exdate.setDate(exdate.getDate() + exp); } var cookieValue = escape(value) + ((exp == null) ? "" : "; expires="+exdate.toUTCString()); document.cookie = name + "=" + cookieValue; }, /* retrieve the value of a cookie */ getCookie: function(name) { var i,x,y,ARRcookies = document.cookie.split(";"); for (i=0;i<ARRcookies.length;i++){ x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")); y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1); x = x.replace(/^\s+|\s+$/g,""); if (x==name){ var value = unescape(y); return value; } } return null; }, /* delete cookies (set expiration time in the past) */ deleleCookie: function() { this.setCookie(name, '', -365); } };
Keep in mind that cookies cannot be retrieved until you refresh the page which is fine for the use case that we are trying to address.
Saving the last visited page
Every time that a page is loaded it fires several events. For the purpose of this example will will focus on the pageshow event. This event fires when the pages is shown which is a good time for us to save our cookie.
1 2 3 4 5 6 7 8 | $(document).bind('pageshow', function(){ // triggered when the page is shown if($.mobile.activePage !== undefined) { var pageId = $.mobile.activePage.attr('id'); // make the cookie expire in 60 min app.cookie.setCookie('lastPage', pageId, 60, true); } }); |
$(document).bind('pageshow', function(){ // triggered when the page is shown if($.mobile.activePage !== undefined) { var pageId = $.mobile.activePage.attr('id'); // make the cookie expire in 60 min app.cookie.setCookie('lastPage', pageId, 60, true); } });
Every time that a page is shown this code will set or update a cookie name lastPage, and it will set the value to the ID of the page (make sure that all your pages have the ID attribute defined).
Restoring the last visited page from the browser cookie
As I mentioned earlier, when you application is minimized Safari Mobile restart and point to the page from where the homescreen shortcut was created, that is the only page where you need to check the value of the cookie. In other words, every time that Safari Mobile restart your application, you will have to check if the cookie exists, if so, you will have to redirect your application to the last visited page if it is different from the main page.
If the pages that comprise your application are all located in a single file, you can simply use the page ID to redirect to the last visited page. For this example we will need to be able to look up the page URL using the page ID. If the ID stored in the cookie is different than the current page ID, then we will need to redirect the application to the URL associated to the stored ID. Here is how:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* Relate the page ID to the actual page URL */ var pageDictionary = { first: 'page1.html', second: 'page2.html' }; /* method to handle the redirection */ function redirectToLastPage(){ var mainPageId = 'first'; var currentPageId = $.mobile.activePage.attr('id'); var lastPageId = app.cookie.getCookie('lastPage'); // these 3 conditions must be valid: // 1. the cookie must exists (lastPageId != null) // 2. the last pages cannot be equal to the current page (currentPageId && currentPageId) // 3. this code must run only in the main application page (currentPageId == mainPage) if(lastPageId != null && lastPageId != currentPageId && currentPageId == mainPageId){ var pageURL = pageDictionary[lastPageId]; //alert(lastPageId +"\n" + currentPageId +"\n" + mainPageId +"\n" + pageURL +"\n"); $.mobile.changePage(pageURL); } // unbind the function itself to make sure the code executes once regardless of the page that has been loaded $(document).unbind('pagebeforeshow', redirectToLastPage); } /* Before the page is show, check the cookie and the list of URLs */ $(document).bind('pagebeforeshow', redirectToLastPage); |
/* Relate the page ID to the actual page URL */ var pageDictionary = { first: 'page1.html', second: 'page2.html' }; /* method to handle the redirection */ function redirectToLastPage(){ var mainPageId = 'first'; var currentPageId = $.mobile.activePage.attr('id'); var lastPageId = app.cookie.getCookie('lastPage'); // these 3 conditions must be valid: // 1. the cookie must exists (lastPageId != null) // 2. the last pages cannot be equal to the current page (currentPageId && currentPageId) // 3. this code must run only in the main application page (currentPageId == mainPage) if(lastPageId != null && lastPageId != currentPageId && currentPageId == mainPageId){ var pageURL = pageDictionary[lastPageId]; //alert(lastPageId +"\n" + currentPageId +"\n" + mainPageId +"\n" + pageURL +"\n"); $.mobile.changePage(pageURL); } // unbind the function itself to make sure the code executes once regardless of the page that has been loaded $(document).unbind('pagebeforeshow', redirectToLastPage); } /* Before the page is show, check the cookie and the list of URLs */ $(document).bind('pagebeforeshow', redirectToLastPage);
Putting everything together
Javascript code: appcropolis.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | var app = {}; app.cookie = { /* set cookies */ setCookie: function(name, value, exp, inMinutes) { var exp = exp == null? 7 : exp; var exdate = new Date(); if(inMinutes != null && inMinutes == true){ exdate.setMinutes(exdate.getMinutes() + exp); } else { exdate.setDate(exdate.getDate() + exp); } var cookieValue = escape(value) + ((exp == null) ? "" : "; expires="+exdate.toUTCString()); document.cookie = name + "=" + cookieValue; }, /* retrieve the value of a cookie */ getCookie: function(name) { var i,x,y,ARRcookies = document.cookie.split(";"); for (i=0;i<ARRcookies.length;i++){ x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")); y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1); x = x.replace(/^\s+|\s+$/g,""); if (x==name){ var value = unescape(y); return value; } } return null; }, /* delete cookies (set expiration time in the past) */ deleleCookie: function() { this.setCookie(name, '', -365); } }; $(document).bind('pageshow', function(){ // triggered when the page is shown var pageId = $.mobile.activePage.attr('id'); // make the cookie expire in 60 min app.cookie.setCookie('lastPage', pageId, 60, true); }); /* Relate the page ID to the actual page URL */ var pageDictionary = { first: 'page1.html', second: 'page2.html' }; /* method to handle the redirection */ function redirectToLastPage(){ var mainPageId = 'first'; var currentPageId = $.mobile.activePage.attr('id'); var lastPageId = app.cookie.getCookie('lastPage'); // these 3 conditions must be valid: // 1. the cookie must exists (lastPageId != null) // 2. the last pages cannot be equal to the current page (currentPageId && currentPageId) // 3. this code must run only in the main application page (currentPageId == mainPage) if(lastPageId != null && lastPageId != currentPageId && currentPageId == mainPageId){ var pageURL = pageDictionary[lastPageId]; //alert(lastPageId +"\n" + currentPageId +"\n" + mainPageId +"\n" + pageURL +"\n"); $.mobile.changePage(pageURL); } // unbind the function itself to make sure the code executes once regardless of the page that has been loaded $(document).unbind('pagebeforeshow', redirectToLastPage); } /* Before the page is show, check the cookie and the list of URLs */ $(document).bind('pagebeforeshow', redirectToLastPage); |
var app = {}; app.cookie = { /* set cookies */ setCookie: function(name, value, exp, inMinutes) { var exp = exp == null? 7 : exp; var exdate = new Date(); if(inMinutes != null && inMinutes == true){ exdate.setMinutes(exdate.getMinutes() + exp); } else { exdate.setDate(exdate.getDate() + exp); } var cookieValue = escape(value) + ((exp == null) ? "" : "; expires="+exdate.toUTCString()); document.cookie = name + "=" + cookieValue; }, /* retrieve the value of a cookie */ getCookie: function(name) { var i,x,y,ARRcookies = document.cookie.split(";"); for (i=0;i<ARRcookies.length;i++){ x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")); y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1); x = x.replace(/^\s+|\s+$/g,""); if (x==name){ var value = unescape(y); return value; } } return null; }, /* delete cookies (set expiration time in the past) */ deleleCookie: function() { this.setCookie(name, '', -365); } }; $(document).bind('pageshow', function(){ // triggered when the page is shown var pageId = $.mobile.activePage.attr('id'); // make the cookie expire in 60 min app.cookie.setCookie('lastPage', pageId, 60, true); }); /* Relate the page ID to the actual page URL */ var pageDictionary = { first: 'page1.html', second: 'page2.html' }; /* method to handle the redirection */ function redirectToLastPage(){ var mainPageId = 'first'; var currentPageId = $.mobile.activePage.attr('id'); var lastPageId = app.cookie.getCookie('lastPage'); // these 3 conditions must be valid: // 1. the cookie must exists (lastPageId != null) // 2. the last pages cannot be equal to the current page (currentPageId && currentPageId) // 3. this code must run only in the main application page (currentPageId == mainPage) if(lastPageId != null && lastPageId != currentPageId && currentPageId == mainPageId){ var pageURL = pageDictionary[lastPageId]; //alert(lastPageId +"\n" + currentPageId +"\n" + mainPageId +"\n" + pageURL +"\n"); $.mobile.changePage(pageURL); } // unbind the function itself to make sure the code executes once regardless of the page that has been loaded $(document).unbind('pagebeforeshow', redirectToLastPage); } /* Before the page is show, check the cookie and the list of URLs */ $(document).bind('pagebeforeshow', redirectToLastPage);
page1.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="apple-mobile-web-app-capable" content="yes"/> <link rel="apple-touch-icon" href="apple-touch-icon.png"> <link rel="apple-touch-startup-image" href="apple-touch-startup-image.png"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> <script type="text/javascript" src="appcropolis.js"></script> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <title>Page #1</title> </head> <body> <div data-role="page" id="first"> <div data-theme="a" data-role="header" data-position="fixed"> <h3>Page #1</h3> </div><!-- /header --> <div data-role="content"> <a data-role="button" href="page1.html">Page #1</a> <a data-role="button" href="page2.html">Page #2</a> </div><!-- /content --> </div><!-- /page --> </body> </html> |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="apple-mobile-web-app-capable" content="yes"/> <link rel="apple-touch-icon" href="apple-touch-icon.png"> <link rel="apple-touch-startup-image" href="apple-touch-startup-image.png"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> <script type="text/javascript" src="appcropolis.js"></script> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <title>Page #1</title> </head> <body> <div data-role="page" id="first"> <div data-theme="a" data-role="header" data-position="fixed"> <h3>Page #1</h3> </div><!-- /header --> <div data-role="content"> <a data-role="button" href="page1.html">Page #1</a> <a data-role="button" href="page2.html">Page #2</a> </div><!-- /content --> </div><!-- /page --> </body> </html>
page2.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="apple-mobile-web-app-capable" content="yes"/> <link rel="apple-touch-icon" href="apple-touch-icon.png"> <link rel="apple-touch-startup-image" href="apple-touch-startup-image.png"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> <script type="text/javascript" src="appcropolis.js"></script> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <title>Page #2</title> </head> <body> <div data-role="page" id="second"> <div data-theme="b" data-role="header" data-position="fixed"> <h1>Page #2</h1> </div><!-- /header --> <div data-role="content"> <a data-role="button" href="page1.html">Page #1</a> <a data-role="button" href="page2.html">Page #2</a> </div><!-- /content --> </div><!-- /page --> </body> </html> |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="apple-mobile-web-app-capable" content="yes"/> <link rel="apple-touch-icon" href="apple-touch-icon.png"> <link rel="apple-touch-startup-image" href="apple-touch-startup-image.png"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> <script type="text/javascript" src="appcropolis.js"></script> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <title>Page #2</title> </head> <body> <div data-role="page" id="second"> <div data-theme="b" data-role="header" data-position="fixed"> <h1>Page #2</h1> </div><!-- /header --> <div data-role="content"> <a data-role="button" href="page1.html">Page #1</a> <a data-role="button" href="page2.html">Page #2</a> </div><!-- /content --> </div><!-- /page --> </body> </html>