Finally a little bit of time to learn something new and put some code up about it! I’ve been playing with jQtouch and PhoneGap for mobile development. Just as all my other stuff, this has an enterprise feel to it. I’ve been trying to come up with some good ways to communicate with a backend database, utilize local storage, and keep many lists of objects without code getting too out of control. Local storage and the backend communication is going to be part two of this. To start, here is how I’m trying to keep pages up to date without too much headache.
The idea is to catch hashchange events, and trigger a load
I catch the hashchange event, and trigger two new events, load Javascript | copy code | ? 1 // global2 var last_hash;
3
4 $(window).bind( 'hashchange',
5 function(){
6 $(window).trigger('unload' + last_hash);
7 last_hash = location.hash;
8 $(window).trigger('load' + location.hash);
9 });
Now I can bind to these new events and find out when individual sections of my page are being loaded by jQtouch.
For example, you have a simple menu:
| HTML | | copy code | | ? |
| 1 | <div id="usersMenu"> |
| 2 | <div class="toolbar"><h1>System Users</h1> |
| 3 | <a href="#" class="button back">Back</a> |
| 4 | </div> |
| 5 | <ul id="usersMenuContent"></ul> |
| 6 | </div> |
This menu has dynamic content that will change from time to time, so we have to load it with an ajax call. You may have a lot of users, and when you select one user, you don’t want all the other menu items taking up memory while you deal with that user.
What we need here is a way to load/unload those user items. What this method does is bind to the load#usersMenu and unload#usersMenu events, and either load data for #usersMenuContent from a data source, or remove the elements from #usersMenuContent.
| Javascript | | copy code | | ? |
| 01 | |
| 02 | $(window).bind('unload#usersMenu', |
| 03 | function() { |
| 04 | $('#usersMenuContent > *').remove(); |
| 05 | }); |
| 06 | |
| 07 | $(window).bind('load#usersMenu', |
| 08 | function() { |
| 09 | var dataArray = loadUsersData(); |
| 10 | for( var i in dataArray ) { |
| 11 | $('<li>' + dataArray[i] + '</li>').appendTo($('#usersMenuContent')); |
| 12 | } |
| 13 | }); |
Theres some psuedo code there to change depending on just what you are loading into the users menu of course. The idea is when your Users Menu loads, it will populate with data, and when you navigate away, the data is removed.
Here is an example we can use. You can view it here.
A simple page that has 3 menus: A primary menu, and two submenus. Each item in the submenus can be clicked on, and loaded into a form for modification(although we don’t implement modification here). Each menu has to load its items, and also remove its items when unloaded.
First, it is important to see the HTML template we will be working with.
| HTML | | copy code | | ? |
| 01 | <body> |
| 02 | <div id="jqt"> |
| 03 | <div id="start"> |
| 04 | <div class="toolbar"><h1>Produce</h1> |
| 05 | </div> |
| 06 | <ul> |
| 07 | <li><a href="#fruitMenu">Fruits</a></li> |
| 08 | <li><a href="#vegetableMenu">Vegetables</a></li> |
| 09 | </ul> |
| 10 | </div> |
| 11 | |
| 12 | <div id="fruitMenu"> |
| 13 | <div class="toolbar"><h1>Fruits</h1> |
| 14 | <a href="#" class="button back">Back</a> |
| 15 | </div> |
| 16 | <ul id="fruitMenuContent"></ul> |
| 17 | </div> |
| 18 | |
| 19 | <div id="vegetableMenu"> |
| 20 | <div class="toolbar"><h1>Vegetables</h1> |
| 21 | <a href="#" class="button back">Back</a> |
| 22 | </div> |
| 23 | <ul id="vegMenuContent"></ul> |
| 24 | </div> |
| 25 | |
| 26 | <div id="fruitClickThru"> |
| 27 | <div class="toolbar"><h1 name="title"></h1> |
| 28 | <a href="#" class="button back">Back</a> |
| 29 | </div> |
| 30 | <ul class="form"> |
| 31 | <label>Name</label><li><input name="name"></li> |
| 32 | <label>Other</label><li><input name="other"></li> |
| 33 | </ul> |
| 34 | </div> |
| 35 | |
| 36 | <div id="vegetableClickThru"> |
| 37 | <div class="toolbar"><h1 name="title"></h1> |
| 38 | <a href="#" class="button back">Back</a> |
| 39 | </div> |
| 40 | <ul class="form"> |
| 41 | <label>Name</label><li><input name="name"></li> |
| 42 | <label>Other</label><li><input name="other"></li> |
| 43 | </ul> |
| 44 | </div> |
| 45 | </div> |
| 46 | </body> |
| 47 | </html> |
| 48 |
Here is the javascript and HTML put together. I trigger a load
| Javascript | | copy code | | ? |
| 001 | <!doctype html> |
| 002 | <html> |
| 003 | <head> |
| 004 | <meta charset="UTF-8" /> |
| 005 | |
| 006 | <title>Dynamic Menus using jQTouch</title> |
| 007 | <style type="text/css" media="screen">@import "jqtouch/jqtouch.css";</style> |
| 008 | <style type="text/css" media="screen">@import "themes/jqt/theme.css";</style> |
| 009 | <script src="jqtouch/jquery-1.4.2.min.js" type="text/javascript" charset="utf-8"></script> |
| 010 | <script src="jqtouch/jqtouch.js" type="application/x-javascript" charset="utf-8"></script> |
| 011 | <script src="jquery.ba-hashchange.js" type="text/javascript" charset="utf-8"></script> |
| 012 | <script type="text/javascript" charset="utf-8"> |
| 013 | |
| 014 | var jQT = new $.jQTouch({ |
| 015 | icon: 'jqtouch.png', |
| 016 | addGlossToIcon: false, |
| 017 | startupScreen: 'jqt_startup.png', |
| 018 | statusBar: 'black' |
| 019 | }); |
| 020 | |
| 021 | /* For this example I think globals are fine. |
| 022 | For real though, the selected item should be stored |
| 023 | inside the object controlling that item type */ |
| 024 | |
| 025 | var activefruit; |
| 026 | var activeveg; |
| 027 | var last_hash; |
| 028 | |
| 029 | |
| 030 | $(function(){ |
| 031 | /* Setup our load events for page changes. The idea is that |
| 032 | we trigger an unload<hash> and load<hash> event whenever |
| 033 | the page location hash changes. The event handlers can |
| 034 | do whatever they want with that information. */ |
| 035 | $(window).bind( 'hashchange', |
| 036 | function(){ |
| 037 | console.log('unloading ' + last_hash); |
| 038 | $(window).trigger('unload' + last_hash); |
| 039 | |
| 040 | /* Handle the previous hash ourselves because some browsers won't support the |
| 041 | hash change event, and others will. So we don't want to modify the plugin |
| 042 | because it may not always be handling the events */ |
| 043 | last_hash = location.hash; |
| 044 | |
| 045 | console.log('loading ' + location.hash); |
| 046 | $(window).trigger('load' + location.hash); |
| 047 | }); |
| 048 | |
| 049 | |
| 050 | /* Handle our fruit menu |
| 051 | Our fruit menu is dynamic. In this example it really isn't, |
| 052 | but real world it would be loaded through an ajax call. Since |
| 053 | the fruit menu is dynamic, we want to bind to the |
| 054 | unload#fruitMenu event, and erase the menu contents when we |
| 055 | navigate away from the page. This makes it easier to keep the |
| 056 | menu free from duplicates, but it also keeps us from having |
| 057 | a lot of DOM objects lying around. */ |
| 058 | $(window).bind('unload#fruitMenu', function() { $('#fruitMenuContent > *').remove(); }); |
| 059 | $(window).bind('load#fruitMenu', |
| 060 | function() { |
| 061 | console.log('Fruit menu loaded'); |
| 062 | |
| 063 | // On a touch event, we set the activefruit global, and link to the fruit click thru |
| 064 | $('<li onclick="activefruit=1;"><a href="#fruitClickThru">Apple</a></li>').appendTo($('#fruitMenuContent')); |
| 065 | $('<li onclick="activefruit=2;"><a href="#fruitClickThru">Banana</a></li>').appendTo($('#fruitMenuContent')); |
| 066 | }); |
| 067 | |
| 068 | /* Handle our vegetable menu |
| 069 | Same as for the fruits. Clean up when the menu is unloaded. */ |
| 070 | $(window).bind('unload#vegetableMenu', function() { $('#vegMenuContent > *').remove(); }); |
| 071 | $(window).bind('load#vegetableMenu', |
| 072 | function() { |
| 073 | console.log('Vegetable menu loaded'); |
| 074 | $('<li onclick="activevegetable=1;"><a href="#vegetableClickThru">Carrots</a></li>').appendTo($('#vegMenuContent')); |
| 075 | $('<li onclick="activevegetable=2;"><a href="#vegetableClickThru">Broccolli</a></li>').appendTo($('#vegMenuContent')); |
| 076 | }); |
| 077 | |
| 078 | |
| 079 | |
| 080 | /* Fill our fruit click thru |
| 081 | Once this section is loaded, we access the activefruit global to determine |
| 082 | what information to populate the form with. Again, in the real world |
| 083 | this info would be grabbed from a database, or an ajax call. */ |
| 084 | $(window).bind('load#fruitClickThru', |
| 085 | function() { |
| 086 | if( activefruit == 1 ) { |
| 087 | $('#fruitClickThru > .form > li > input[name=name]').val('Apple'); |
| 088 | $('#fruitClickThru > .form > li > input[name=other]').val('Red'); |
| 089 | $('#fruitClickThru > .toolbar > :header').html('Apple'); |
| 090 | } else if( activefruit == 2 ) { |
| 091 | $('#fruitClickThru > .form > li > input[name=name]').val('Banana'); |
| 092 | $('#fruitClickThru > .form > li > input[name=other]').val('Yellow'); |
| 093 | $('#fruitClickThru > .toolbar > :header').html('Banana'); |
| 094 | } |
| 095 | }); |
| 096 | |
| 097 | /* Fill out vegetable click thru |
| 098 | Same as the fruit sections. We aren't bother to bind to the unload |
| 099 | event here because the form fields are static and we replace their |
| 100 | values during each load event anyway. */ |
| 101 | $(window).bind('load#vegetableClickThru', |
| 102 | function() { |
| 103 | if( activevegetable == 1 ) { |
| 104 | $('#vegetableClickThru > .form > li > input[name=name]').val('Carrots'); |
| 105 | $('#vegetableClickThru > .form > li > input[name=other]').val('Orange'); |
| 106 | $('#vegetableClickThru > .toolbar > :header').html('Carrots'); |
| 107 | } else if( activevegetable == 2 ) { |
| 108 | $('#vegetableClickThru > .form > li > input[name=name]').val('Broccolli'); |
| 109 | $('#vegetableClickThru > .form > li > input[name=other]').val('Green'); |
| 110 | $('#vegetableClickThru > .toolbar > :header').html('Broccolli'); |
| 111 | } |
| 112 | }); |
| 113 | }); |
| 114 | </script> |
| 115 | </head> |
| 116 | <body> |
| 117 | <div id="jqt"> |
| 118 | <div id="start"> |
| 119 | <div class="toolbar"><h1>Produce</h1> |
| 120 | </div> |
| 121 | <ul> |
| 122 | <li><a href="#fruitMenu">Fruits</a></li> |
| 123 | <li><a href="#vegetableMenu">Vegetables</a></li> |
| 124 | </ul> |
| 125 | </div> |
| 126 | |
| 127 | <div id="fruitMenu"> |
| 128 | <div class="toolbar"><h1>Fruits</h1> |
| 129 | <a href="#" class="button back">Back</a> |
| 130 | </div> |
| 131 | <ul id="fruitMenuContent"></ul> |
| 132 | </div> |
| 133 | |
| 134 | <div id="vegetableMenu"> |
| 135 | <div class="toolbar"><h1>Vegetables</h1> |
| 136 | <a href="#" class="button back">Back</a> |
| 137 | </div> |
| 138 | <ul id="vegMenuContent"></ul> |
| 139 | </div> |
| 140 | |
| 141 | <div id="fruitClickThru"> |
| 142 | <div class="toolbar"><h1 name="title"></h1> |
| 143 | <a href="#" class="button back">Back</a> |
| 144 | </div> |
| 145 | <ul class="form"> |
| 146 | <label>Name</label><li><input name="name"></li> |
| 147 | <label>Other</label><li><input name="other"></li> |
| 148 | </ul> |
| 149 | </div> |
| 150 | |
| 151 | <div id="vegetableClickThru"> |
| 152 | <div class="toolbar"><h1 name="title"></h1> |
| 153 | <a href="#" class="button back">Back</a> |
| 154 | </div> |
| 155 | <ul class="form"> |
| 156 | <label>Name</label><li><input name="name"></li> |
| 157 | <label>Other</label><li><input name="other"></li> |
| 158 | </ul> |
| 159 | </div> |
| 160 | </div> |
| 161 | </body> |
| 162 | </html> |
| 163 |
Moving forward
The next step is to have the menus load from the local cache. If the cache doesn’t have the requested information, then it should be retrieved. This way when you load a menu, you can always show a spinner and if the cache has been invalidated, new information will automatically be populated. But that is for next time.
