80d70a6a9e887a8606251bb518d388d96380d413
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.0.1pre / docs / pages / page-dynamic.html
1 <!DOCTYPE html>
2 <html>
3         <head>
4         <meta charset="utf-8">
5         <meta name="viewport" content="width=device-width, initial-scale=1">
6         <title>jQuery Mobile Docs - Dynamically Injecting Pages</title>
7         <link rel="stylesheet"  href="../../css/themes/default/" />
8         <link rel="stylesheet" href="../_assets/css/jqm-docs.css"/>
9         <script src="../../js/jquery.js"></script>
10         <script src="../../experiments/themeswitcher/jquery.mobile.themeswitcher.js"></script>
11         <script src="../_assets/js/jqm-docs.js"></script>
12         <script src="../../js/"></script>
13 </head>
14 <body>
15
16         <div data-role="page" class="type-interior">
17
18                 <div data-role="header" data-theme="f">
19                 <h1>Dynamically Injecting Pages</h1>
20                 <a href="../../" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a>
21         </div><!-- /header -->
22
23         <div data-role="content">
24                 <div class="content-primary">
25                         <h2>jQuery Mobile and Dynamic Page Generation</h2>
26                         <p>jQuery Mobile allows pages to be pulled into the DOM dynamically via its default click hijacking behavior, or through manual calls to $.mobile.changePage(). This is great for applications that generate HTML pages/fragments on the server-side, but there are sometimes cases where an application needs to dynamically generate page content on the client-side from JSON or some other format. This may be necessary for bandwidth/performance reasons, or because it is the data format of choice for the server they are interacting with.</p>
27                         <p>For applications that need to generate page markup on the client-side, it's important to know about the notifications that are triggered during a $.mobile.changePage() call because they can be used as hooks into the navigation system that will allow you to generate your content at the appropriate time.</p>
28                         <p>A call to changePage() will usually trigger the following event notifications:</p>
29                         <ul>
30                           <li>pagebeforechange
31                             <ul>
32                               <li>Fired off before any page loading or transition.</li>
33                               <li>NOTE: This event was formerly known as &quot;beforechangepage&quot;.</li>
34                         </ul>
35                           </li>
36                           <li>pagechange
37                             <ul>
38                               <li>Fired off after all page loading and transitions.</li>
39                               <li>NOTE: this event was formerly known as &quot;changepage&quot;.</li>
40                         </ul>
41                           </li>
42                           <li>pagechangefailed
43                             <ul>
44                               <li>Fired off if an error has occurred while attempting to dynamically load a new page.                     </li>
45                         </ul>
46                           </li>
47                   </ul>
48                         <p>These notifications are triggered on  the parent container element ($.mobile.pageContainer) of pages, and will bubble all the way up to the document element and window.</p>
49 <p>For applications wishing to inject pages, or radically modify the content of an existing page, based on some non-HTML data, such as JSON or in-memory JS object, the pagebeforechange event is very useful since it gives you a hook for analyzing the URL or page element the application is being asked to load or switch to, and short-circuit the default changePage() behavior by simply calling preventDefault() on the pagebeforechange event.</p>
50 <p>To illustrate this technique, take a look at this <a href="dynamic-samples/sample-reuse-page.html" rel="external">working sample</a>. In this sample, the main page starts off with a list of categories that the user can navigate into. The actual items in each category are stored in a JavaScript object in memory, for illustrative purposes, but the data can really come from anywhere.</p>
51 <pre><code>
52 var categoryData = {
53         animals: {
54                 name: &quot;Animals&quot;,
55                 description: &quot;All your favorites from aardvarks to zebras.&quot;,
56                 items: [
57                         {
58                                 name: &quot;Pets&quot;,
59                         },
60                         {
61                                 name: &quot;Farm Animals&quot;,
62                         },
63                         {
64                                 name: &quot;Wild Animals&quot;,
65                         }
66                 ]
67         },
68         colors: {
69                 name: &quot;Colors&quot;,
70                 description: &quot;Fresh colors from the magic rainbow.&quot;,
71                 items: [
72                         {
73                                 name: &quot;Blue&quot;,
74                         },
75                         {
76                                 name: &quot;Green&quot;,
77                         },
78                         {
79                                 name: &quot;Orange&quot;,
80                         },
81                         {
82                                 name: &quot;Purple&quot;,
83                         },
84                         {
85                                 name: &quot;Red&quot;,
86                         },
87                         {
88                                 name: &quot;Yellow&quot;,
89                         },
90                         {
91                                 name: &quot;Violet&quot;,
92                         }
93                 ]
94         },
95         vehicles: {
96                 name: &quot;Vehicles&quot;,
97                 description: &quot;Everything from cars to planes.&quot;,
98                 items: [
99                         {
100                                 name: &quot;Cars&quot;,
101                         },
102                         {
103                                 name: &quot;Planes&quot;,
104                         },
105                         {
106                                 name: &quot;Construction&quot;,
107                         }
108                 ]
109         }
110 };
111 </code></pre>
112 <p>The application uses links with urls that contain a hash that tells the application what category items to display:</p>
113 <pre>
114 <code>
115         &lt;h2&gt;Select a Category Below:&lt;/h2&gt;
116         &lt;ul data-role=&quot;listview&quot; data-inset=&quot;true&quot;&gt;
117         &lt;li&gt;&lt;a href=&quot;#category-items?category=animals&quot;&gt;Animals&lt;/a&gt;&lt;/li&gt;
118         &lt;li&gt;&lt;a href=&quot;#category-items?category=colors&quot;&gt;Colors&lt;/a&gt;&lt;/li&gt;
119         &lt;li&gt;&lt;a href=&quot;#category-items?category=vehicles&quot;&gt;Vehicles&lt;/a&gt;&lt;/li&gt;
120     &lt;/ul&gt;
121 </code>
122 </pre>
123 <p>Internally, when the user clicks on one of these links, the application intercepts the internal $.mobile.changePage() call that is invoked by the frameworks' default link hijacking behavior. It then analyzes the URL for the page about to be loaded, and then decides whether or not it should handle the loading itself, or to let the normal changePage() code handle things.</p>
124 <p>The application was able to insert itself into the changePage() flow by binding to the &quot;pagebeforechange&quot; event at the document level:</p>
125 <pre>
126 <code>
127 // Listen for any attempts to call changePage().
128 $(document).bind( &quot;pagebeforechange&quot;, function( e, data ) {
129
130         // We only want to handle changePage() calls where the caller is
131         // asking us to load a page by URL.
132         if ( typeof data.toPage === &quot;string&quot; ) {
133
134                 // We are being asked to load a page by URL, but we only
135                 // want to handle URLs that request the data for a specific
136                 // category.
137                 var u = $.mobile.path.parseUrl( data.toPage ),
138                         re = /^#category-item/;
139
140                 if ( u.hash.search(re) !== -1 ) {
141
142                         // We're being asked to display the items for a specific category.
143                         // Call our internal method that builds the content for the category
144                         // on the fly based on our in-memory category data structure.
145                         showCategory( u, data.options );
146
147                         // Make sure to tell changePage() we've handled this call so it doesn't
148                         // have to do anything.
149                         e.preventDefault();
150                 }
151         }
152 });
153 </code>
154 </pre>
155 <p>So why listen at the document level? In short, because of deep-linking. We need our binding to be active before the jQuery Mobile framework initializes and decides how to process the initial URL that invoked the application.</p>
156 <p>When the callback for the &quot;pagebeforechange&quot; binding is invoked, the 2nd argument to the callback will be a data object that contains the arguments that were passed to the initial $.mobile.changePage() call. The properties of this object are as follows:</p>
157 <ul>
158   <li>toPage
159     <ul>
160       <li>Can be either a jQuery collection object containing the page to be transitioned to, *OR* a URL reference for a page to be loaded/transitioned to.</li>
161     </ul>
162   </li>
163   <li>options
164     <ul>
165       <li>Object containing the options that were passed in by the caller of the $.mobile.changePage() function.</li>
166       <li>A list of the options can be found <a href="../api/methods.html">here</a>.</li>
167       </ul>
168   </li>
169   </ul>
170 <p>For our sample application, we are only interested in changePage() calls where URLs are initially passed in, so the first thing our callback does is check the type for the toPage. Next, with the help of some URL parsing utilities, it checks to make sure if the URL contains a hash that we are interested in handling ourselves. If so, it then calls an application function called showCategory() which will dynamically create the content for the category specified by the URL hash, and then it calls preventDefault() on the event.</p>
171 <p>Calling preventDefault() on a pagebeforechange event causes the originating $.mobile.changePage() call to exit without performing any work. Calling the preventDefault() method on the event is the equivalent of telling jQuery Mobile that you have handled the changePage() request yourself.</p>
172 <p>If preventDefault() is not called, changePage() will continue on processing as it normally does. One thing to point out about the data object that is passed into our callback, is that any changes you make to the toPage property, or options properties, will affect changePage() processing if preventDefault() is not called. So for example, if I wanted to redirect or map a specific URL to another internal/external page, my callback could simply set the data.toPage property in the callback to the URL or DOM element of the page to redirect to. Likewise, I could set, or un-set any option from within my callback, and changePage() would use the new settings.</p>
173 <p>So now that we know how to intercept changePage() calls, let's take a closer look at how this sample actually generates the markup for a page. Our example actually uses, or I should say, re-uses the same page to display each of the categories. Each time one of our special links is clicked, the function showCategory() gets invoked:</p>
174 <pre><code>
175 // Load the data for a specific category, based on
176 // the URL passed in. Generate markup for the items in the
177 // category, inject it into an embedded page, and then make
178 // that page the current active page.
179 function showCategory( urlObj, options )
180 {
181         var categoryName = urlObj.hash.replace( /.*category=/, &quot;&quot; ),
182
183                 // Get the object that represents the category we
184                 // are interested in. Note, that at this point we could
185                 // instead fire off an ajax request to fetch the data, but
186                 // for the purposes of this sample, it's already in memory.
187                 category = categoryData[ categoryName ],
188
189                 // The pages we use to display our content are already in
190                 // the DOM. The id of the page we are going to write our
191                 // content into is specified in the hash before the '?'.
192                 pageSelector = urlObj.hash.replace( /\?.*$/, &quot;&quot; );
193
194         if ( category ) {
195                 // Get the page we are going to dump our content into.
196                 var $page = $( pageSelector ),
197
198                         // Get the header for the page.
199                         $header = $page.children( &quot;:jqmData(role=header)&quot; ),
200
201                         // Get the content area element for the page.
202                         $content = $page.children( &quot;:jqmData(role=content)&quot; ),
203
204                         // The markup we are going to inject into the content
205                         // area of the page.
206                         markup = &quot;&lt;p&gt;&quot; + category.description + &quot;&lt;/p&gt;&lt;ul data-role='listview' data-inset='true'&gt;&quot;,
207
208                         // The array of items for this category.
209                         cItems = category.items,
210
211                         // The number of items in the category.
212                         numItems = cItems.length;
213
214                 // Generate a list item for each item in the category
215                 // and add it to our markup.
216                 for ( var i = 0; i &lt; numItems; i++ ) {
217                         markup += &quot;&lt;li&gt;&quot; + cItems[i].name + &quot;&lt;/li&gt;&quot;;
218                 }
219                 markup += &quot;&lt;/ul&gt;&quot;;
220
221                 // Find the h1 element in our header and inject the name of
222                 // the category into it.
223                 $header.find( &quot;h1&quot; ).html( category.name );
224
225                 // Inject the category items markup into the content element.
226                 $content.html( markup );
227
228                 // Pages are lazily enhanced. We call page() on the page
229                 // element to make sure it is always enhanced before we
230                 // attempt to enhance the listview markup we just injected.
231                 // Subsequent calls to page() are ignored since a page/widget
232                 // can only be enhanced once.
233                 $page.page();
234
235                 // Enhance the listview we just injected.
236                 $content.find( &quot;:jqmData(role=listview)&quot; ).listview();
237
238                 // We don't want the data-url of the page we just modified
239                 // to be the url that shows up in the browser's location field,
240                 // so set the dataUrl option to the URL for the category
241                 // we just loaded.
242                 options.dataUrl = urlObj.href;
243
244                 // Now call changePage() and tell it to switch to
245                 // the page we just modified.
246                 $.mobile.changePage( $page, options );
247         }
248 }
249 </code></pre>
250 <p>In our sample app, the hash of the URL we handle contains 2 parts:</p>
251 <pre><code>
252 #category-items?category=vehicles
253 </code></pre>
254 <p>The first part, before the '?' is actually the id of the page to write content into, the part after the '?' is info the app uses to figure out what data it should use when generating the markup for the page. The first thing showCategory() does is deconstruct this hash to extract out the id of the page to write content into, and the name of the category it should use to get the correct set of data from our in-memory JavaScript category object. After it figures out what category data to use, it then generates the markup for the category, and then injects it into the header and content area of the page, wiping out any other markup that previously existed in those elements.</p>
255 <p>After it injects the markup, it then calls the appropriate jQuery Mobile widget calls to enhance the list markup it just injected. This is what turns the normal list markup into a fully styled listview with all its behaviors.</p>
256 <p>Once that's done, it then calls $.mobile.changePage(), passing it the DOM element of the page we just modified, to tell the framework that it wants to show that page.</p>
257 <p>Now an interesting problem here is that jQuery Mobile typically updates the browser's location hash with the URL associated with the page it is showing. Because we are re-using the same page for each category, this wouldn't be ideal, because the URL for that page has no specific category info associated with it. To get around this problem, showCategory() simply sets the dataUrl property on the options object it passes into changePage() to tell it to display our original URL instead.</p>
258 <p>That's the sample in a nutshell. It should be noted that this particular sample and its usage is not a very good example of an app that degrades gracefully when JavaScript is turned off. That means it probably won't work very well on C-Grade browsers. We will be posting other examples that demonstrate how to degrade gracefully in the future. Check this <a href="http://jquerymobile.com/test/docs/pages/dynamic-samples/">page</a> for updates.</p>
259       </div>
260                 <!--/content-primary -->
261
262                                 <div class="content-secondary">
263
264                                         <div data-role="collapsible" data-collapsed="true" data-theme="b" data-content-theme="d">
265
266                                                         <h3>More in this section</h3>
267
268                                                         <ul data-role="listview" data-theme="c" data-dividertheme="d">
269
270                                                                 <li data-role="list-divider">Pages &amp; Dialogs</li>
271                                                                 <li><a href="page-anatomy.html">Anatomy of a page</a></li>
272                                                                 <li><a href="page-template.html" data-ajax="false">Single page template</a></li>
273                                                                 <li><a href="multipage-template.html" data-ajax="false">Multi-page template</a></li>
274                                                                 <li><a href="page-titles.html">Page titles</a></li>
275                                                                 <li><a href="page-links.html">Linking pages</a></li>
276                                                                 <li><a href="page-transitions.html" data-ajax="false">Page transitions</a></li>
277                                                                 <li><a href="page-dialogs.html">Dialogs</a></li>
278                                                                 <li><a href="page-cache.html">Prefetching &amp; caching pages</a></li>
279                                                                 <li><a href="page-navmodel.html">Ajax, hashes &amp; history</a></li>
280                                                                 <li data-theme="a"><a href="page-dynamic.html">Dynamically injecting pages</a></li>
281                                                                 <li><a href="page-scripting.html">Scripting pages</a></li>
282                                                                 <li><a href="phonegap.html">PhoneGap apps</a></li>
283                                                                 <li><a href="touchoverflow.html">touchOverflow feature</a></li>
284                                                                 <li><a href="pages-themes.html">Theming pages</a></li>
285                                                         </ul>
286                                         </div>
287                                 </div>
288
289                         </div><!-- /content -->
290
291                         <div data-role="footer" class="footer-docs" data-theme="c">
292                                         <p>&copy; 2011 The jQuery Project</p>
293                         </div>
294
295                         </div><!-- /page -->
296
297                         </body>
298                         </html>