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