|
1 <!DOCTYPE html> |
|
2 <html lang="en"> |
|
3 <head> |
|
4 <meta charset="utf-8"> |
|
5 <title>Example: Building a UI with AsyncQueue</title> |
|
6 <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
7 <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
8 <link rel="stylesheet" href="../assets/css/main.css"> |
|
9 <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
10 <link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
11 <script src="../../build/yui/yui-min.js"></script> |
|
12 |
|
13 </head> |
|
14 <body> |
|
15 <!-- |
|
16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
17 --> |
|
18 <div id="doc"> |
|
19 <div id="hd"> |
|
20 <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
21 </div> |
|
22 |
|
23 |
|
24 <h1>Example: Building a UI with AsyncQueue</h1> |
|
25 <div class="yui3-g"> |
|
26 <div class="yui3-u-3-4"> |
|
27 <div id="main"> |
|
28 <div class="content"><style scoped> |
|
29 #init { |
|
30 margin-top: 1em; |
|
31 } |
|
32 |
|
33 #demo .yui3-module { |
|
34 position: relative; |
|
35 width: 28em; |
|
36 } |
|
37 #demo .yui3-module .yui3-hd, |
|
38 #demo .yui3-module .yui3-bd, |
|
39 #demo .yui3-module .yui3-ft { |
|
40 margin: 0; |
|
41 padding: 1ex 1em; |
|
42 } |
|
43 #demo .yui3-module .yui3-hd { |
|
44 background: #406ED9; |
|
45 } |
|
46 #demo .yui3-module .yui3-hd h4 { |
|
47 color: #fff; |
|
48 margin: 0; |
|
49 } |
|
50 #demo .yui3-module .yui3-bd { |
|
51 background: #ABCEFF; |
|
52 border-left: 1px solid #7A97BB; |
|
53 border-right: 1px solid #7A97BB; |
|
54 height: 5em; |
|
55 padding-top: 4.5em; |
|
56 position: relative; |
|
57 overflow: hidden; |
|
58 text-align: center; |
|
59 } |
|
60 #demo .yui3-module .yui3-ft { |
|
61 background: #fff; |
|
62 border: 1px solid #7A97BB; |
|
63 border-top-color: #ccc; |
|
64 padding-right: 25px; |
|
65 } |
|
66 #demo .yui3-module .yui3-status { |
|
67 margin: 0; |
|
68 padding: 0 25px 0 0; |
|
69 height: 1.3em; |
|
70 } |
|
71 #demo .yui3-module .yui3-nav { |
|
72 background: #fff; |
|
73 border-bottom: 1px solid #ccc; |
|
74 left: 0; |
|
75 padding: .5em; |
|
76 position: absolute; |
|
77 width: 27em; |
|
78 } |
|
79 #demo .yui3-module .yui3-nav ul, |
|
80 #demo .yui3-module .yui3-nav li { |
|
81 list-style: none; |
|
82 margin: 0; |
|
83 padding: 0; |
|
84 } |
|
85 #demo .yui3-module .yui3-nav a { |
|
86 color: #ffa928; |
|
87 } |
|
88 #demo .yui3-module .working { |
|
89 background: #fff url(http://l.yimg.com/a/i/nt/ic/ut/bsc/busyarr_1.gif) no-repeat 26em 50%; |
|
90 } |
|
91 </style> |
|
92 |
|
93 |
|
94 <div class="intro"> |
|
95 <p>This example illustrates how to break up the initial rendering of an application UI into queued code chunks, yielding back to the browser regularly to draw portions of the UI as they become ready.</p> |
|
96 |
|
97 <p><em>Note</em>: This method should be reserved for apps constructing complex DOM structures. While the DOM structure contained in this example is not complex, some artificial delays are injected to simulate process-intensive operations that would normally cause such delays.</p> |
|
98 </div> |
|
99 |
|
100 <div class="example yui3-skin-sam"> |
|
101 <div id="demo"> |
|
102 <p>The module will be inserted here. <em>Click the button below</em>.</p> |
|
103 </div> |
|
104 |
|
105 <button id="init">Initialize Application</button> |
|
106 |
|
107 |
|
108 <script type="text/javascript"> |
|
109 YUI().use("node", "transition","async-queue", function (Y) { |
|
110 |
|
111 var MyApp = { |
|
112 NAME : 'Asynchronous Queue Demo', |
|
113 |
|
114 q : new Y.AsyncQueue(), |
|
115 |
|
116 nodes : { |
|
117 root : null, |
|
118 status : null, |
|
119 nav : null, |
|
120 content : null, |
|
121 foot : null |
|
122 }, |
|
123 |
|
124 render : function (container) { |
|
125 if (MyApp.nodes.root) { |
|
126 MyApp.q.stop(); |
|
127 } |
|
128 |
|
129 // artificial delays have been inserted to simulate _renderNav or |
|
130 // _renderContent being process intensive and taking a while to complete |
|
131 MyApp.q.add( |
|
132 // pass the container param to the callback using Y.bind |
|
133 Y.bind(MyApp._renderFramework, MyApp, container), |
|
134 {fn: function () {}, timeout: 700}, // artificial delay |
|
135 MyApp._renderNav, |
|
136 {fn: function () {}, timeout: 700}, // artificial delay |
|
137 MyApp._renderContent).run(); |
|
138 }, |
|
139 |
|
140 setStatus : function (message,working) { |
|
141 MyApp.nodes.status.setHTML(message); |
|
142 |
|
143 MyApp.nodes.foot[working?'addClass':'removeClass']('working'); |
|
144 }, |
|
145 |
|
146 _renderFramework : function (container) { |
|
147 var root = MyApp.nodes.root = Y.one(container); |
|
148 |
|
149 root.setHTML( |
|
150 '<div class="yui3-module">'+ |
|
151 '<div class="yui3-hd">'+ |
|
152 '<h4>'+MyApp.NAME+'</h4>'+ |
|
153 '</div>'+ |
|
154 '<div class="yui3-bd">'+ |
|
155 '<div class="yui3-nav"></div>'+ |
|
156 '<div class="yui3-content"></div>'+ |
|
157 '</div>'+ |
|
158 '<div class="yui3-ft">'+ |
|
159 '<p class="yui3-status"></p>'+ |
|
160 '</div>'+ |
|
161 '</div>'); |
|
162 |
|
163 MyApp.nodes.status = root.one('p.yui3-status'); |
|
164 MyApp.nodes.nav = root.one('.yui3-nav'); |
|
165 MyApp.nodes.content = root.one('.yui3-content'); |
|
166 MyApp.nodes.foot = root.one('.yui3-ft'); |
|
167 |
|
168 MyApp.nodes.nav.setStyle('top','-30px'); |
|
169 MyApp.nodes.content.setStyle('opacity',0); |
|
170 |
|
171 MyApp.setStatus('Loading...',true); |
|
172 }, |
|
173 |
|
174 _renderNav : function () { |
|
175 var nav = MyApp.nodes.nav; |
|
176 nav.append( |
|
177 '<ul class="yui3-g">'+ |
|
178 '<li class="yui3-u-1-4"><a href="#">Nav Lorem</a></li>'+ |
|
179 '<li class="yui3-u-1-4"><a href="#">Nav Ipsum</a></li>'+ |
|
180 '<li class="yui3-u-1-4"><a href="#">Nav Dolor</a></li>'+ |
|
181 '<li class="yui3-u-1-4"><a href="#">Nav Sit</a></li>'+ |
|
182 '</ul>'); |
|
183 |
|
184 nav.transition({ |
|
185 top: 0, |
|
186 duration: .3 |
|
187 }); |
|
188 |
|
189 // Stub some navigation behavior for the example |
|
190 nav.delegate('click', function (e) { |
|
191 e.preventDefault(); |
|
192 MyApp.nodes.content |
|
193 .setHTML('<p>Clicked on ' + this.get('text') + '</p>'); |
|
194 }, 'a'); |
|
195 }, |
|
196 |
|
197 _renderContent : function () { |
|
198 MyApp.nodes.content |
|
199 .setHTML('<p>[ App content here ]</p>') |
|
200 .transition({ |
|
201 opacity: 1, |
|
202 duration: .8 |
|
203 }); |
|
204 |
|
205 MyApp.setStatus('App initialized',false); |
|
206 } |
|
207 }; |
|
208 |
|
209 Y.one('#init').on('click',function (e) { |
|
210 e.preventDefault(); |
|
211 this.set('text','Re-initialize Application'); |
|
212 |
|
213 MyApp.render('#demo'); |
|
214 }); |
|
215 |
|
216 // expose the example structure |
|
217 YUI.example = { MyApp : MyApp }; |
|
218 |
|
219 }); |
|
220 </script> |
|
221 |
|
222 </div> |
|
223 |
|
224 <h3>The Markup</h3> |
|
225 <p>The markup will start with just a placeholder element for our application.</p> |
|
226 |
|
227 <pre class="code prettyprint"><div id="demo"> |
|
228 <p>The module will be inserted here. <em>Click the button below</em>.</p> |
|
229 </div> |
|
230 |
|
231 <button id="init">Initialize Application</button></pre> |
|
232 |
|
233 |
|
234 <p>The markup will eventually evolve to the following as the script runs (indented for readability):</p> |
|
235 |
|
236 <pre class="code prettyprint"><div id="demo"> |
|
237 <div class="yui3-module"> |
|
238 <div class="yui3-hd"> |
|
239 <h4>AsyncQueue Demo</h4> |
|
240 </div> |
|
241 <div class="yui3-bd"> |
|
242 <div class="yui3-nav"> |
|
243 <ul class="yui3-g"> |
|
244 <li class="yui3-u-1-4"><a href="#">Nav Lorem</a></li> |
|
245 <li class="yui3-u-1-4"><a href="#">Nav Ipsum</a></li> |
|
246 <li class="yui3-u-1-4"><a href="#">Nav Dolor</a></li> |
|
247 <li class="yui3-u-1-4"><a href="#">Nav Sit</a></li> |
|
248 </ul> |
|
249 </div> |
|
250 <div class="yui3-content"> |
|
251 <p>[ App content here ]</p> |
|
252 </div> |
|
253 </div> |
|
254 <div class="yui3-ft"> |
|
255 <p class="yui3-status">(status message here)</p> |
|
256 </div> |
|
257 </div> |
|
258 </div> |
|
259 |
|
260 <button id="init">Re-initialize Application</button></pre> |
|
261 |
|
262 |
|
263 <h3>The CSS</h3> |
|
264 <p>Some CSS is added to make it look like an application.</p> |
|
265 |
|
266 <pre class="code prettyprint"><style scoped> |
|
267 #init { |
|
268 margin-top: 1em; |
|
269 } |
|
270 |
|
271 #demo .yui3-module { |
|
272 position: relative; |
|
273 width: 28em; |
|
274 } |
|
275 #demo .yui3-module .yui3-hd, |
|
276 #demo .yui3-module .yui3-bd, |
|
277 #demo .yui3-module .yui3-ft { |
|
278 margin: 0; |
|
279 padding: 1ex 1em; |
|
280 } |
|
281 #demo .yui3-module .yui3-hd { |
|
282 background: #406ED9; |
|
283 } |
|
284 #demo .yui3-module .yui3-hd h4 { |
|
285 color: #fff; |
|
286 margin: 0; |
|
287 } |
|
288 #demo .yui3-module .yui3-bd { |
|
289 background: #ABCEFF; |
|
290 border-left: 1px solid #7A97BB; |
|
291 border-right: 1px solid #7A97BB; |
|
292 height: 5em; |
|
293 padding-top: 4.5em; |
|
294 position: relative; |
|
295 overflow: hidden; |
|
296 text-align: center; |
|
297 } |
|
298 #demo .yui3-module .yui3-ft { |
|
299 background: #fff; |
|
300 border: 1px solid #7A97BB; |
|
301 border-top-color: #ccc; |
|
302 padding-right: 25px; |
|
303 } |
|
304 #demo .yui3-module .yui3-status { |
|
305 margin: 0; |
|
306 padding: 0 25px 0 0; |
|
307 height: 1.3em; |
|
308 } |
|
309 #demo .yui3-module .yui3-nav { |
|
310 background: #fff; |
|
311 border-bottom: 1px solid #ccc; |
|
312 left: 0; |
|
313 padding: .5em; |
|
314 position: absolute; |
|
315 width: 27em; |
|
316 } |
|
317 #demo .yui3-module .yui3-nav ul, |
|
318 #demo .yui3-module .yui3-nav li { |
|
319 list-style: none; |
|
320 margin: 0; |
|
321 padding: 0; |
|
322 } |
|
323 #demo .yui3-module .yui3-nav a { |
|
324 color: #ffa928; |
|
325 } |
|
326 #demo .yui3-module .working { |
|
327 background: #fff url(http://l.yimg.com/a/i/nt/ic/ut/bsc/busyarr_1.gif) no-repeat 26em 50%; |
|
328 } |
|
329 </style></pre> |
|
330 |
|
331 |
|
332 <h3>Example application structure</h3> |
|
333 <p>For this example, we'll create a simple application that we'll contain under the <code>MyApp</code> namespace. The basic structure of the namespace will be as follows:</p> |
|
334 |
|
335 <pre class="code prettyprint">YUI().use("node", "transition", "async-queue", function (Y) { |
|
336 |
|
337 var MyApp = { |
|
338 // the name of the application |
|
339 NAME : "AsyncQueue Demo", |
|
340 |
|
341 // rendering AsyncQueue |
|
342 q : new Y.AsyncQueue(), |
|
343 |
|
344 // cache of frequently used nodes in the DOM structure |
|
345 nodes : { |
|
346 root : null, |
|
347 status : null, |
|
348 nav : null, |
|
349 content : null, |
|
350 foot : null |
|
351 }, |
|
352 |
|
353 /*** Public API methods ***/ |
|
354 // draws the UI in the specified container |
|
355 render : function (container) { ... }, |
|
356 |
|
357 // update the status bar at the bottom of the app |
|
358 setStatus : function (message,working) { ... }, |
|
359 |
|
360 |
|
361 /*** private methods ***/ |
|
362 // adds the basic app skeleton to the page |
|
363 _renderFramework : function () { ... }, |
|
364 |
|
365 // populates the navigation section |
|
366 _renderNav : function () { ... }, |
|
367 |
|
368 // populates the content section |
|
369 _renderContent : function () { ... } |
|
370 }; |
|
371 |
|
372 });</pre> |
|
373 |
|
374 |
|
375 <p>The <code>MyApp.render</code> function will add the rendering methods to the <code>MyApp.q</code> AsyncQueue and set it in motion. Each of the methods will be executed in turn, yielding back to the browser between steps. So as each piece of the UI is assembled, the browser is given the opportunity to draw it.</p> |
|
376 |
|
377 <pre class="code prettyprint">... |
|
378 render : function (container) { |
|
379 // If the application is currently rendered somewhere, destroy it first |
|
380 // by clearing the queue and adding the destroy method to run before |
|
381 // the default rendering operations. |
|
382 if (MyApp.nodes.root) { |
|
383 MyApp.q.stop(); |
|
384 |
|
385 MyApp.q.add( |
|
386 MyApp.destroy |
|
387 ); |
|
388 } |
|
389 |
|
390 // Add the rendering operations to the ops.render queue and call run() |
|
391 MyApp.q.add( |
|
392 // pass the container param to the callback using Y.bind |
|
393 Y.bind(MyApp._renderFramework, MyApp, container), |
|
394 MyApp._renderNav, |
|
395 MyApp._renderContent).run(); |
|
396 }, |
|
397 ...</pre> |
|
398 |
|
399 |
|
400 <p>If there are any process-intensive operations in the rendering steps, the UI generated in all <em>previous</em> steps will have been drawn by the browser before the heavy lifting begins. This way, the user will be shown a part of the UI and can begin to develop an understanding of its structure and operation while the rest of it is being constructed.</p> |
|
401 |
|
402 <h3>A note on artificial delays and animation</h3> |
|
403 <p>In this example, rather than include code that would spike your CPU, delays were simulated by inserting AsyncQueue callbacks with a timeout and a function that does nothing. There is a distinct difference between a delay caused by code execution and a delay caused by <code>setTimeout</code>. In the former case, the browser is busy and likely won't respond to user events (such as clicks) until the executing code has completed. In the latter, any number of JavaScript event threads may execute to completion in the intervening time.</p> |
|
404 |
|
405 <h3>Full Script Source</h3> |
|
406 <p>The complete code for this example includes the artificial delays added to <code>MyApp.q</code> in the <code>render</code> method.</p> |
|
407 |
|
408 <pre class="code prettyprint"><script type="text/javascript"> |
|
409 YUI().use("node", "transition","async-queue", function (Y) { |
|
410 |
|
411 var MyApp = { |
|
412 NAME : 'Asynchronous Queue Demo', |
|
413 |
|
414 q : new Y.AsyncQueue(), |
|
415 |
|
416 nodes : { |
|
417 root : null, |
|
418 status : null, |
|
419 nav : null, |
|
420 content : null, |
|
421 foot : null |
|
422 }, |
|
423 |
|
424 render : function (container) { |
|
425 if (MyApp.nodes.root) { |
|
426 MyApp.q.stop(); |
|
427 } |
|
428 |
|
429 // artificial delays have been inserted to simulate _renderNav or |
|
430 // _renderContent being process intensive and taking a while to complete |
|
431 MyApp.q.add( |
|
432 // pass the container param to the callback using Y.bind |
|
433 Y.bind(MyApp._renderFramework, MyApp, container), |
|
434 {fn: function () {}, timeout: 700}, // artificial delay |
|
435 MyApp._renderNav, |
|
436 {fn: function () {}, timeout: 700}, // artificial delay |
|
437 MyApp._renderContent).run(); |
|
438 }, |
|
439 |
|
440 setStatus : function (message,working) { |
|
441 MyApp.nodes.status.setHTML(message); |
|
442 |
|
443 MyApp.nodes.foot[working?'addClass':'removeClass']('working'); |
|
444 }, |
|
445 |
|
446 _renderFramework : function (container) { |
|
447 var root = MyApp.nodes.root = Y.one(container); |
|
448 |
|
449 root.setHTML( |
|
450 '<div class="yui3-module">'+ |
|
451 '<div class="yui3-hd">'+ |
|
452 '<h4>'+MyApp.NAME+'</h4>'+ |
|
453 '</div>'+ |
|
454 '<div class="yui3-bd">'+ |
|
455 '<div class="yui3-nav"></div>'+ |
|
456 '<div class="yui3-content"></div>'+ |
|
457 '</div>'+ |
|
458 '<div class="yui3-ft">'+ |
|
459 '<p class="yui3-status"></p>'+ |
|
460 '</div>'+ |
|
461 '</div>'); |
|
462 |
|
463 MyApp.nodes.status = root.one('p.yui3-status'); |
|
464 MyApp.nodes.nav = root.one('.yui3-nav'); |
|
465 MyApp.nodes.content = root.one('.yui3-content'); |
|
466 MyApp.nodes.foot = root.one('.yui3-ft'); |
|
467 |
|
468 MyApp.nodes.nav.setStyle('top','-30px'); |
|
469 MyApp.nodes.content.setStyle('opacity',0); |
|
470 |
|
471 MyApp.setStatus('Loading...',true); |
|
472 }, |
|
473 |
|
474 _renderNav : function () { |
|
475 var nav = MyApp.nodes.nav; |
|
476 nav.append( |
|
477 '<ul class="yui3-g">'+ |
|
478 '<li class="yui3-u-1-4"><a href="#">Nav Lorem</a></li>'+ |
|
479 '<li class="yui3-u-1-4"><a href="#">Nav Ipsum</a></li>'+ |
|
480 '<li class="yui3-u-1-4"><a href="#">Nav Dolor</a></li>'+ |
|
481 '<li class="yui3-u-1-4"><a href="#">Nav Sit</a></li>'+ |
|
482 '</ul>'); |
|
483 |
|
484 nav.transition({ |
|
485 top: 0, |
|
486 duration: .3 |
|
487 }); |
|
488 |
|
489 // Stub some navigation behavior for the example |
|
490 nav.delegate('click', function (e) { |
|
491 e.preventDefault(); |
|
492 MyApp.nodes.content |
|
493 .setHTML('<p>Clicked on ' + this.get('text') + '</p>'); |
|
494 }, 'a'); |
|
495 }, |
|
496 |
|
497 _renderContent : function () { |
|
498 MyApp.nodes.content |
|
499 .setHTML('<p>[ App content here ]</p>') |
|
500 .transition({ |
|
501 opacity: 1, |
|
502 duration: .8 |
|
503 }); |
|
504 |
|
505 MyApp.setStatus('App initialized',false); |
|
506 } |
|
507 }; |
|
508 |
|
509 Y.one('#init').on('click',function (e) { |
|
510 e.preventDefault(); |
|
511 this.set('text','Re-initialize Application'); |
|
512 |
|
513 MyApp.render('#demo'); |
|
514 }); |
|
515 |
|
516 // expose the example structure |
|
517 YUI.example = { MyApp : MyApp }; |
|
518 |
|
519 }); |
|
520 </script></pre> |
|
521 |
|
522 </div> |
|
523 </div> |
|
524 </div> |
|
525 |
|
526 <div class="yui3-u-1-4"> |
|
527 <div class="sidebar"> |
|
528 |
|
529 |
|
530 |
|
531 <div class="sidebox"> |
|
532 <div class="hd"> |
|
533 <h2 class="no-toc">Examples</h2> |
|
534 </div> |
|
535 |
|
536 <div class="bd"> |
|
537 <ul class="examples"> |
|
538 |
|
539 |
|
540 <li data-description="This example employs AsyncQueue to incrementally construct an application interface; this illustrates the approach you'd take to allow chunked rendering of the UI in a process-intensive application."> |
|
541 <a href="queue-app.html">Building a UI with AsyncQueue</a> |
|
542 </li> |
|
543 |
|
544 |
|
545 </ul> |
|
546 </div> |
|
547 </div> |
|
548 |
|
549 |
|
550 |
|
551 </div> |
|
552 </div> |
|
553 </div> |
|
554 </div> |
|
555 |
|
556 <script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
557 <script>prettyPrint();</script> |
|
558 |
|
559 <script> |
|
560 YUI.Env.Tests = { |
|
561 examples: [], |
|
562 project: '../assets', |
|
563 assets: '../assets/async-queue', |
|
564 name: 'queue-app', |
|
565 title: 'Building a UI with AsyncQueue', |
|
566 newWindow: '', |
|
567 auto: false |
|
568 }; |
|
569 YUI.Env.Tests.examples.push('queue-app'); |
|
570 |
|
571 </script> |
|
572 <script src="../assets/yui/test-runner.js"></script> |
|
573 |
|
574 |
|
575 |
|
576 </body> |
|
577 </html> |