1 <!DOCTYPE html>
     
2 <html lang="en">
     
3 <head>
     
4     <title>RetroV Demo and Tutorial</title>
     
5     <meta charset="utf-8">
     
6     <meta name="viewport" content="width=device-width, initial-scale=1">
     
7     <style>
     
8         body {
     
9             background: #000; color: #FFF;
    
10             font-size: 18px;
    
11             max-width: 700px;
    
12             margin: auto;
    
13         }
    
14         a, a:hover, a:visited { color: #F0A; }
    
15         pre { padding: 1em; font-size: 0.8em; margin-left: 2em; overflow: auto; }
    
16         pre,code { color: #F4F; }
    
17         h2 { color: #acff47; margin-top: 2em; border-bottom: 2px solid #acff47; }
    
18         h3 { color: #47ffdd; margin-top: 3em; }
    
19         strong { color: #47ffdd; }
    
20         .container {
    
21             padding: 1em;
    
22             border: 1px solid #0cf;
    
23             margin-top: 10px;
    
24         }
    
25         .aqua {
    
26             background: #0CF; color: black; padding: 3px;
    
27             display: inline-block; margin: 5px;
    
28             animation: 0.5s attention;
    
29             position: relative; /* for attention anim */
    
30             border: 1px solid #000;
    
31          }
    
32         .orange {
    
33             background: orange; color: black; padding: 3px;
    
34             display: inline-block; margin: 5px;
    
35             animation: 0.5s attention;
    
36             position: relative; /* for attention anim */
    
37             border: 1px solid #000;
    
38         }
    
39         @keyframes attention { from { top: -5px; } to { top: 0; } }
    
40         .bold { font-weight: bold; }
    
41         button.toggle_state {
    
42             display: block;
    
43             background: #db01db;
    
44             margin: 10px;
    
45             color: white; 
    
46             font-weight: bold;
    
47             border-style: none;
    
48             border-radius: 8px;
    
49             padding: 5px 8px;
    
50             float: right;
    
51         }
    
52         button.toggle_state:hover { background: #ff4aff; }
    
53     </style>
    
54     <script src="retrov.js"></script>
    
55 </head>
    
56 <body>
    
57 
    58 <center> <!-- A center tag! Now that's retro! -->
    
59     <h1>RetroV demo and tutorial</h1>
    
60     <img src="./retrov.svg" alt="RetroV 1970s colors svg logo">
    
61 </center>
    
62 
    63 <script>
    
64 function make_container(){
    
65     var script = document.currentScript;
    
66     var output = document.createElement('div');
    
67     output.className = "container";
    
68     script.after(output);
    
69 //    script.parentNode.insertBefore(output, script);
    
70     window.test_container = output;
    
71     return output;
    
72 }
    
73 
    74 function toggle_state(c){
    
75     var after = false;
    
76     var states = c.states;
    
77 
    78     var btn = document.createElement('button');
    
79     btn.innerHTML = 'Toggle'; // "2" meaning "second"
    
80     btn.className = 'toggle_state';
    
81     btn.addEventListener('click', function(){
    
82         var s = after ? 'before' : 'after';
    
83         after = !after; //toggle
    
84         RV.render(c, states[s]);
    
85     });
    
86     c.before(btn);
    
87 //    c.append(btn);
    
88 }
    
89 
    90 var c; // re-used a bunch to hold example containers
    
91 </script>
    
92 
    93 <h2>Contents</h2>
    
94 <ul>
    
95     <li><a href="#start">Getting started</a>
    
96         <ul>
    
97             <li><a href="#startnote">Note</a></li>
    
98         </ul>
    
99     </li>
   
100     <li><a href="#simplerender">Simple rendering
   
101          <ul>
   
102             <li><a href="#text">Text</a></li>
   
103             <li><a href="#html">HTML nodes</a></li>
   
104             <li><a href="#siblings">Arrays of siblings</a></li>
   
105             <li><a href="#props">Element properties</a></li>
   
106             <li><a href="#classes">Class shorthand</a></li>
   
107             <li><a href="#styleprop">The style property</a></li>
   
108             <li><a href="#htmllit">HTML literals</a></li>
   
109             <li><a href="#nestednodes">Nested HTML nodes</a></li>
   
110         </ul>
   
111     </li>
   
112     <li><a href="#dynamic">Dynamic updates
   
113          <ul>
   
114             <li><a href="#changingnode">Changing an HTML node property</a></li>
   
115             <li><a href="#changingcls">Same, but with class shorthand</a></li>
   
116             <li><a href="#addingitem">Adding an item to a list</a></li>
   
117             <li><a href="#changingsib">Changing a sibling element in a list</a></li>
   
118             <li><a href="#removingsib">Removing a sibling element from a list</a></li>
   
119             <li><a href="#nullingsib">"Nulling" a sibling element from a list</a></li>
   
120             <li><a href="#nullingchild">"Nulling" a child</a></li>
   
121             <li><a href="#false">A <code>false</code> means "no change"</a></li>
   
122             <li><a href="#oncreate">Special event: <code>oncreate</code></a></li>
   
123             <li><a href="#rvid">Special property: <code>rvid</code></a></li>
   
124         </ul>
   
125     </li>
   
126     <li><a href="#cookbook">Cookbook
   
127          <ul>
   
128             <li><a href="#renderingfn">Rendering with simple nested functions</a></li>
   
129             <li><a href="#renderingfn2">Rendering with higher-order functions</a></li>
   
130             <li><a href="#stateclosures">Storing state in function closures</a></li>
   
131             <li><a href="#renderfalse">Controlling rendering with <code>false</code></a></li>
   
132             <li><a href="#input">Text input</a></li>
   
133             <li><a href="#textarea">Textarea updates</a></li>
   
134             <li><a href="#focus">Element focus</a></li>
   
135         </ul>
   
136     </li>
   
137 </ul>
   
138 
   139 
   140 <h2 id="start">Getting started</h2>
   
141 
   142 <p>You can add the RetroV library to a page
   
143 with nothing more than a script tag like so:
   
144 
   145 <pre>
   
146 <script src="retrov.js"></script>
   
147 </pre>
   
148 
   149 <p>This exposes a global <code>RV</code> object with a <code>render()</code>
   
150 method. The first render parameter is a target DOM element to render into.  The
   
151 second parameter is an element or tree of "virtual node" data to be
   
152 rendered.<p>
   
153 
   154 <pre>
   
155 RV.render(document.body, 'Hello world.");
   
156 
   157 RV.render(document.getElementById('my_panel'), ['div', ...]);
   
158 </pre>
   
159 
   160 <p>RetroV can be used to render HTML from JavaScript once as a
   
161 template engine. Or it can be used to create interactive UIs with
   
162 functional "components".</p>
   
163 
   164 <p>The rest of this page is a series of increasingly interesting examples
   
165 that run live in the browser.</p>
   
166 
   167 <h3 id="startnote">Note</h3>
   
168 <p>This page makes heavy use of two utility functions. Both are part of this
   
169 page and have <em>nothing</em> to do with RetroV itself:
   
170 <ul>
   
171     <li><code>make_container()</code> creates a <div> and returns a
   
172         reference to it.</li>
   
173     <li><code>toggle_state()</code> creates a <strong>Toggle</strong> button
   
174         which renders "before" and "after" VNodes.</li>
   
175 </ul>
   
176 </p>
   
177 
   178 <p>With that out of the way, let's see some examples!</p>
   
179 
   180 <h2 id="simplerender">Simple rendering</h2>
   
181 
   182 
   183 <h3 id="text">Text</h3>
   
184 <p>Strings and numbers are rendered as text nodes.</p>
   
185 <script data-mirror>
   
186 c = make_container();
   
187 
   188 RV.render(c, 'Hello world.');
   
189 </script>
   
190 
   191 <h3 id="html">HTML nodes</h3>
   
192 <p>In RetroV, an HTML tag is represented by an array with the tag name
   
193 as the first element of the array. Here is a paragraph tag with a
   
194 text node child:</p>
   
195 <script data-mirror>
   
196 c = make_container();
   
197 
   198 RV.render(c, ['p', 'Hello paragraph.']);
   
199 </script>
   
200 
   201 <h3 id="siblings">Arrays of siblings</h3>
   
202 <p>You can supply more than one element in an array. (<strong>Note:</strong>
   
203 you cannot have an array of strings because that would be indistinguishable
   
204 from an HTML tag.)</p>
   
205 <script data-mirror>
   
206 c = make_container();
   
207 
   208 RV.render(c, [
   
209     ['p', 'Paragraph one.'],
   
210     ['p', 'Paragraph two.'],
   
211 ]);
   
212 </script>
   
213 
   214 <h3 id="props">Element properties</h3>
   
215 <p>HTML tags can have a properties object as the second item.  Let's make this
   
216 paragraph tag more interesting by assigning a class via the
   
217 <code>class</code> property.
   
218 After this object, any children follow as usual.</p>
   
219 <script data-mirror>
   
220 c = make_container();
   
221 
   222 RV.render(c, ['p', {'class': 'aqua'}, 'Classy paragraph.']);
   
223 </script>
   
224 
   225 <p>Note: Sharp-eyed JavaScript developers will recognize that 'class' is a reserved
   
226 word and is not used as the actual property in the JS interface to the DOM.
   
227 RetroV automatically converts
   
228 <code>class</code> to <code>className</code> (and <code>for</code> to <code>htmlFor</code>)
   
229 for you. You are welcome to use the second form directly.</p>
   
230 
   231 
   232 <h3 id="classes">Class shorthand</h3>
   
233 <p>Because it's so common to apply a class name (or two) to
   
234 a tag, you can also use the "CSS selector" style shorthand
   
235 to apply one or more classes.</p>
   
236 <p>In addition, just a class name without a tag creates an
   
237 implicit <div>.</p>
   
238 <script data-mirror>
   
239 c = make_container();
   
240 
   241 RV.render(c, [
   
242     ['span.aqua', 'aqua'],
   
243     ['span.orange', 'orange'],
   
244     ['span.orange.bold', 'orange bold'],
   
245     ['.aqua', 'implicit div tag'],
   
246 ]);
   
247 </script>
   
248 
   249 <h3 id="styleprop">The style property</h3>
   
250 
   251 <p>Though <em>excessive</em> use of inline styles can get messy real fast,
   
252 sometimes they're unavoidable.</p>
   
253 
   254 <p>RetroV accepts a <code>style</code> property object containing the styles
   
255 you wish to set using standard JavaScript names (in which CSS properties
   
256 with hyphens are replaced with camelCase).</p>
   
257 
   258 <script data-mirror>
   
259 c = make_container();
   
260 
   261 RV.render(c,
   
262     ['div', {
   
263         style: {
   
264             color: 'orange',
   
265             textShadow: '1px 1px #000, 3px 3px #0cf',
   
266             fontSize: '2em',
   
267         }},
   
268     "Fancy!"
   
269     ]
   
270 );
   
271 </script>
   
272 
   273 <h3 id="htmllit">HTML literals</h3>
   
274 
   275 <p>It is inevitable that you will eventually need to include some raw
   
276 markup in your interface. RetroV supports this. If it detects that you have an
   
277 element name that starts with "<code><</code>", it assumes the whole
   
278 string is raw verbatim HTML.</p>
   
279 
   280 <p>There are lots of ways to use this feature, including cloning another
   
281 element (and its children) by stealing its <code>innerHTML</code> content.</p>
   
282 
   283 <p>And let's face it, sometimes it's just easier to produce an HTML string
   
284 than create a nested array data structure and that's okay!</p>
   
285 
   286 <p><strong>Note:</strong> Only <em>one</em> top-level item will be created,
   
287 but it can have as many children as you like. (This may mean you need to
   
288 make a container span or div. Sorry. It has to be this way to keep the
   
289 child element counts in sync across changes.)</p>
   
290 
   291 <script data-mirror>
   
292 c = make_container();
   
293 
   294 var svg_smiley = '<svg xmlns="http://www.w3.org/2000/svg" width="215.123" height="215.123" viewBox="0 0 56.918 56.918"><circle cx="28.459" cy="28.459" r="28.084" fill="#faf100" stroke="#070707" stroke-width=".75"/><ellipse cx="18.074" cy="20.509" rx="4.972" ry="7.382"/><ellipse cx="38.712" cy="20.509" rx="4.972" ry="7.382"/><path d="M11.739 38.311c7.173 11.435 26.554 11.585 33.419 0" fill="none" stroke="#070707" stroke-width="1.55"/></svg>';
   
295 
   296 RV.render(c, ['.aqua',
   
297     ['<div style="font-size: 1.7em">Have a nice day!</div>'],
   
298     [svg_smiley],
   
299 ]);
   
300 </script>
   
301 
   302 
   303 
   304 <h3 id="nestednodes">Nested HTML nodes</h3>
   
305 <p>So far, we've seen tags with just a single text node child.
   
306 But arbitrary HTML structures can be nested as children.</p>
   
307 <script data-mirror>
   
308 c = make_container();
   
309 
   310 RV.render(c,
   
311     ['.aqua',
   
312         'Enjoy',
   
313         ['.orange',
   
314             'those',
   
315             ['.aqua', 'antique'],
   
316         ],
   
317         ['.orange',
   
318             ['.aqua.bold', 'spicy'],
   
319             ['.aqua', 'rats'],
   
320         ],
   
321     ]
   
322 );
   
323 </script>
   
324 
   325 
   326 <h2 id="dynamic">Dynamic updates</h2>
   
327 
   328 <p>When you render more than once to the same DOM element, RetroV will check
   
329 for changes in the "virtual DOM" VNodes from the previous rendering and apply
   
330 any differences to the real DOM.</p>
   
331 
   332 <h3 id="changingnode">Changing an HTML node property</h3>
   
333 
   334 <p>Here, the <code>class</code> (class) property is being updated.
   
335 Click the <strong>Toggle</strong> button to see the change applied.
   
336 Click it again to revert to the original state.</p>
   
337 <script data-mirror>
   
338 c = make_container();
   
339 
   340 c.states = {
   
341     before: ['div', {'class':'aqua'}, 'Hello world'],
   
342     after:  ['div', {'class':'orange'}, 'Hello world'],
   
343 };
   
344 
   345 RV.render(c, c.states.before);
   
346 
   347 toggle_state(c);
   
348 </script>
   
349 
   350 <h3 id="changingcls">Same, but with class shorthand</h3>
   
351 
   352 <p>Change class property via shorthand and implicit div tags.</p>
   
353 <script data-mirror>
   
354 c = make_container();
   
355 
   356 c.states = {
   
357     before: ['.aqua','Hello again'],
   
358     after:  ['.orange','Hello again'],
   
359 };
   
360 
   361 RV.render(c, c.states.before);
   
362 
   363 toggle_state(c);
   
364 </script>
   
365 
   366 
   367 <h3 id="addingitem">Adding an item to a list</h3>
   
368 
   369 <p>In the example below, a third <div> element is added to the
   
370 list. Clicking <strong>Toggle</strong> a <em>second</em> time removes the
   
371 element and so on.</p>
   
372 <p>Note how the new element "jumps" when it is added. This is done with
   
373 a CSS animation when the element is added to the DOM.
   
374 By watching which elements jump, you can see which nodes RetroV is 
   
375 adding/replacing and which ones are being left alone.
   
376 (Property changes and text node changes won't make an element
   
377 jump, though. Only elements being added to the DOM.)</p>
   
378 
   379 <script data-mirror>
   
380 c = make_container();
   
381 
   382 c.states = {
   
383     before: [['.aqua','A'],['.orange','B']],
   
384     after:  [['.aqua','A'],['.orange','B'],['.aqua','C']],
   
385 };
   
386 
   387 RV.render(c, c.states.before);
   
388 
   389 toggle_state(c);
   
390 </script>
   
391 
   392 
   393 <h3 id="changingsib">Changing a sibling element in a list</h3>
   
394 
   395 <p>Since the middle item is being changed from an (implicit) div to
   
396 a span tag, it will be replaced completely. It will "jump" as
   
397 the new element replaces the existing one. Notice how the two
   
398 elements on either side remain untouched since they have not changed.</p>
   
399 <script data-mirror>
   
400 c = make_container();
   
401 
   402 c.states = {
   
403     before: [['.aqua','A'],['.aqua','B'],['.aqua','C']],
   
404     after:  [['.aqua','A'],['span.orange','Q'],['.aqua','C']],
   
405 };
   
406 
   407 RV.render(c, c.states.before);
   
408 
   409 toggle_state(c);
   
410 </script>
   
411 
   412 
   413 <h3 id="removingsib">Removing a sibling element from a list</h3>
   
414 
   415 <p>Completely removing an element from a list will cause all elements
   
416 after it to be re-evaluated. Since the tags alternate between divs and
   
417 spans, the new list won't line up with the old list and all the
   
418 following tags will end up being replaced entirely.</p>
   
419 
   420 <script data-mirror>
   
421 c = make_container();
   
422 
   423 c.states = {
   
424     before: [['.aqua','A'],['span.orange','B'],['.aqua','C'],['span.aqua','D']],
   
425     after:  [['.aqua','A'],['.aqua','C'],['span.aqua','D']],
   
426 };
   
427 
   428 RV.render(c, c.states.before);
   
429 
   430 toggle_state(c);
   
431 </script>
   
432 
   433 
   434 <h3 id="nullingsib">"Nulling" a sibling element from a list</h3>
   
435 
   436 <p>On the other hand, replacing an element with the value <code>null</code>
   
437 will render an HTML comment placeholder, which keeps subsequent items lined up
   
438 in their original position.</p>
   
439 <p>Compare the "jumping" between this and the previous example. This one
   
440 is much more efficient since only the affected item is redrawn.</p>
   
441 
   442 <script data-mirror>
   
443 c = make_container();
   
444 
   445 c.states = {
   
446     before: [['.aqua','A'],['span.orange','B'],['.aqua','C'],['span.aqua','D']],
   
447     after:  [['.aqua','A'],null,['.aqua','C'],['span.aqua','D']],
   
448 };
   
449 
   450 RV.render(c, c.states.before);
   
451 
   452 toggle_state(c);
   
453 </script>
   
454 
   455 <h3 id="nullingchild">"Nulling" a child</h3>
   
456 
   457 <p>You can use <code>null</code> anywhere you might otherwise have an element
   
458 (not just in an array, like above). Here, it is taking the place of several
   
459 child nodes. Notice how the last element does not jump since it is still in the
   
460 same child position.</p>
   
461 
   462 <script data-mirror>
   
463 c = make_container();
   
464 
   465 c.states = {
   
466     before: ['.aqua','Flowers',
   
467         ['.orange','Roses'],
   
468         ['.orange','Sunflowers'],
   
469         ['.orange','Lavender'],
   
470     ],
   
471     after: ['.aqua','Flowers',
   
472         null,
   
473         null,
   
474         ['.orange','Lavender'],
   
475     ]
   
476 };
   
477 
   478 RV.render(c, c.states.before);
   
479 
   480 toggle_state(c);
   
481 </script>
   
482 
   483 <h3 id="false">A <code>false</code> means "no change"</h3>
   
484 
   485 <p>This looks just like the <code>null</code> example above, except
   
486 with <code>false</code> in place of two of the items. The visual difference
   
487 is that the items remain. This is because as long as <code>false</code>
   
488 is given for a position, it will be <em>left alone</em>.</p>
   
489 
   490 <p>Note how the flowers "jump" when you click the <strong>Toggle</strong>
   
491 button a <em>second</em> time (as a non-false value is toggled back in).</p>
   
492 
   493 <p>See the Cookbook section below for an example of using <code>false</code>
   
494 to control rendering.</p>
   
495 
   496 <script data-mirror>
   
497 c = make_container();
   
498 
   499 c.states = {
   
500     before: ['.aqua','Flowers',
   
501         ['.orange','Roses'],
   
502         ['.orange','Sunflowers'],
   
503         ['.orange','Lavender'],
   
504     ],
   
505     after: ['.aqua','Flowers',
   
506         false,
   
507         false,
   
508         ['.orange','Lavender'],
   
509     ]
   
510 };
   
511 
   512 RV.render(c, c.states.before);
   
513 
   514 toggle_state(c);
   
515 </script>
   
516 
   517 <h3 id="oncreate">Special event: <code>oncreate</code></h3>
   
518 
   519 <p>It <em>should</em> be fairly rare, but sometimes you cannot avoid
   
520 directly manipulating DOM elements. RetroV has a special pseudo-event
   
521 called <code>oncreate</code> which takes a function. When the actual
   
522 DOM element for that virtual element is created and attached to the
   
523 DOM, the function is called and passed a reference to the element.
   
524 </p>
   
525 
   526 <p>In this example, we want to animate a div by manipulating the
   
527 element directly. We <em>could</em> do this by re-rendering the
   
528 entire scene through <code>RV.render()</code>, but that would be
   
529 wasteful.</p>
   
530 
   531 <script data-mirror>
   
532 c = make_container();
   
533 
   534 function twitchy_start(elem){
   
535     var twitch=false;
   
536     setInterval(
   
537         function(){
   
538             elem.style.marginLeft = (twitch?'10px':'0');
   
539             twitch=!twitch;
   
540         },
   
541         800, // fraction of a second
   
542     );
   
543 }
   
544 
   545 RV.render(c, ['.twitchy',
   
546     {
   
547         oncreate: twitchy_start,
   
548     },
   
549     'Twitchy Div!',
   
550 ]);
   
551 </script>
   
552 
   553 <h3 id="rvid">Special property: <code>rvid</code></h3>
   
554 
   555 <p>RetroV
   
556 recognizes that you'll occasionally need to store a
   
557 reference to an element it has created.
   
558 You could easily do this yourself with the <code>oncreate</code> callback property above, but the result would be annoying boilerplate code.</p>
   
559 
   560 <p>An <code>rvid</code> property lets you pick a name
   
561 for an element and RetroV will store a reference to it
   
562 in its <code>id</code> namespace.
   
563 
   564 <p>In the example below, <code>rvid: 'neighbor'</code>
   
565 creates a reference to a <div> element as
   
566 <code>RV.id.neighbor</code>, which can be used as soon
   
567 as the element has been rendered.</p>
   
568 
   569 <script data-mirror>
   
570 c = make_container();
   
571 
   572 function rvid_click(){
   
573     RV.id.neighbor.classList.toggle('orange');
   
574 }
   
575 
   576 RV.render(c, [
   
577     ['.aqua', {rvid:'neighbor'}, 'Neighbor'],
   
578     ['button', { onclick: rvid_click }, 'Click me!'],
   
579 ]);
   
580 </script>
   
581 
   582 <h2 id="cookbook">Cookbook</h2>
   
583 
   584 <p>Ideas for solutions to common problems.</p>
   
585 
   586 <h3 id="renderingfn">Rendering with simple nested functions</h3>
   
587 
   588 <p>Here you can see that I've broken down the task of drawing lists of
   
589 numbers into drawing the list and drawing the numbers. It's a silly
   
590 example, of course, but the principle applies nicely to a larger and
   
591 more complex interface.</p>
   
592 
   593 <p>It's worth pointing out that in this case, RetroV doesn't know anything
   
594 about these functions. It's just seeing the returned data they generate.</p>
   
595 
   596 <script data-mirror>
   
597 c = make_container();
   
598 
   599 function draw_number(num){
   
600     return ['.orange', num];
   
601 }
   
602 
   603 function draw_number_list(num1, num2){
   
604     return ['.aqua',
   
605         draw_number(num1),
   
606         draw_number(num2),
   
607     ];
   
608 }
   
609 
   610 RV.render(c, 
   
611     ['.orange', 'My number lists:',
   
612         draw_number_list(42, 13),
   
613         draw_number_list(11, 999),
   
614     ]
   
615 );
   
616 </script>
   
617 
   618 
   619 <h3 id="renderingfn2">Rendering with higher-order functions</h3>
   
620 
   621 <p>Creating interfaces by generating data also plays extremely well with
   
622 <em>functional programming</em> concepts, such as using <code>map()</code> to
   
623 render an array with a function.</p>
   
624 
   625 <p>(Map is a higher-order function because it takes another function
   
626 as input.)</p>
   
627 
   628 <script data-mirror>
   
629 c = make_container();
   
630 
   631 var fruits = [
   
632     'Apple',
   
633     'Pear',
   
634     'Lime',
   
635     'Strawberry',
   
636 ];
   
637 
   638 function draw_fruit(num){
   
639     return ['.aqua', num];
   
640 }
   
641 
   642 RV.render(c, ['.orange', 'My fruit:', fruits.map(draw_fruit) ]);
   
643 </script>
   
644 
   645 <h3 id="stateclosures">Storing state in function closures</h3>
   
646 
   647 <p>RetroV is built with the philosophy that storing and updating state
   
648 should be separate from rendering the result of that state.</p>
   
649 
   650 <p>Thus, "components" which track a <em>lot</em> of state are antithetical to
   
651 the intention of RetroV. Having said that, it is nice to be able to
   
652 keep track of simple things locally sometimes.</p>
   
653 
   654 <p>Note that the <code>feed()</code> function in this example doesn't
   
655 just update the counter, it also re-renders <em>everything</em>.
   
656 The whole point of using a VDOM is to let the library detect changes
   
657 and efficiently perform only the updates that are needed.</p>
   
658 
   659 <script data-mirror>
   
660 // This example needs a unique container variable.
   
661 var c1 = make_container();
   
662 
   663 function Animal(name, color){
   
664     var fed = 0;
   
665 
   666     function feed(){
   
667         fed++;
   
668         render_animals();
   
669     }
   
670 
   671     return function draw_animal(){
   
672         return ['div', {'class':color},
   
673             name + ' has been fed: ' + fed +
   
674                 (fed === 1 ? ' time.' : ' times.'),
   
675             ['button', {onclick:feed}, 'Feed'],
   
676         ];
   
677     };
   
678 }
   
679 
   680 var tiger = Animal('Tiger', 'orange');
   
681 var fish  = Animal('Fish', 'aqua');
   
682 
   683 function render_animals(){
   
684     RV.render(c1, [tiger(), fish()]);
   
685 }
   
686 
   687 render_animals();
   
688 </script>
   
689 
   690 <h3 id="renderfalse">Controlling rendering with <code>false</code></h3>
   
691 
   692 <p>You may wish to have a section of a page only render 
   
693 (or <em>stop</em> rendering) when some condition has been met.</p>
   
694 
   695 <p>This example has a "component" that renders exactly twice. It does
   
696 this by returning <code>false</code> after the second render.</p>
   
697 
   698 <p>Noticec how the area's render counter will continue to go up, but the
   
699 "component" will stop incrementing at 2.</p>
   
700 
   701 <script data-mirror>
   
702 // This example needs a unique container variable.
   
703 var c2 = make_container();
   
704 
   705 function RendersTwice(){
   
706     var render_count = 0;
   
707 
   708     return function draw(){
   
709         render_count++;
   
710 
   711         if(render_count > 2){
   
712             return false;
   
713         }
   
714 
   715         return ['.aqua', 'Renders Twice: ' + render_count];
   
716     };
   
717 }
   
718 
   719 var area_render_count = 0;
   
720 var renders_twice =  RendersTwice();
   
721 
   722 function render_area(){
   
723     area_render_count++;
   
724 
   725     RV.render(c2, ['.orange', 'Area rendered: ' + area_render_count,
   
726         renders_twice(),
   
727         ['button', {onclick:render_area}, 'Re-Render'],
   
728     ]);
   
729 }
   
730 
   731 render_area();
   
732 </script>
   
733 
   734 <h3 id="input">Text input</h3>
   
735 
   736 <p>This isn't a special technique, but just an example of an extremely
   
737 common interaction that deserves an example somewhere.</p>
   
738 
   739 <p>There are countless ways to add abstraction to handle the tedious
   
740 redundancy of form elements. This example does not demonstrate any.</p>
   
741 
   742 <p>Keep in mind that RetroV is a rendering library. It has absolutely no
   
743 opinion about how you save/load/update data.</p>
   
744 
   745 <script data-mirror>
   
746 // This example needs a unique container variable.
   
747 var c3 = make_container();
   
748 
   749 var my_data = {
   
750     name: "Nothing",
   
751     age: 0,
   
752 };
   
753 
   754 function update_name(e){
   
755     my_data.name = e.target.value;
   
756     render_form();
   
757 }
   
758 
   759 function update_age(e){
   
760     my_data.age = e.target.value;
   
761     render_form();
   
762 }
   
763 
   764 function render_form(){
   
765     RV.render(c3, ['form.aqua',
   
766         ['label', 'Name:',
   
767             ['input', {
   
768                 type: 'text',
   
769                 value: my_data.name,
   
770                 oninput: update_name,
   
771             }],
   
772         ],
   
773         ['label', 'Age:',
   
774             ['input', {
   
775                 type: 'text',
   
776                 value: my_data.age,
   
777                 oninput: update_age,
   
778             }],
   
779         ],
   
780         ['p', '"I am ' + my_data.name + ', ' + my_data.age + ' years old."'],
   
781     ]); // end of form
   
782 }
   
783 
   784 render_form();
   
785 </script>
   
786 
   787 
   788 <h3 id="textarea">Textarea updates</h3>
   
789 
   790 <p>This is another note that isn't actually specific to
   
791 RetroV, but deserves an example because it's a confusing
   
792 topic.</p>
   
793 
   794 <p>The HTML textarea element doesn't have a
   
795 <code>value</code> <em>attribute</em>. Instead, we write a
   
796 textarea's value as content in the the tag like so:
   
797 <code><texarea>Hello!</texarea></code>.
   
798 However, when interacting with the element, its
   
799 <code>value</code> <em>property</em> contains the current
   
800 value of the textarea.</p>
   
801 
   802 <p>Play with this:</p>
   
803 
   804 <script data-mirror>
   
805 // This example needs a unique container variable.
   
806 var c4 = make_container();
   
807 
   808 var foo_count = 0;
   
809 function draw_textarea(){
   
810     foo_count++; // change with each draw
   
811     var txt = "Foo " + foo_count;
   
812 
   813     RV.render(c4, ['.aqua',
   
814         ['textarea', txt],
   
815         ['textarea', {value: txt}],
   
816         ['button', {onclick: draw_textarea}, "Update!"],
   
817     ]);
   
818 }
   
819 
   820 draw_textarea();
   
821 </script>
   
822 
   823 <p>At first, both examples update as you hit the Update
   
824 button.  However, once you type into the textareas, only the
   
825 one that updates the <code>value</code> property will
   
826 update. That's because whatever you type becomes the
   
827 <code>value</code> and supersedes the <em>text content</em>
   
828 in the tag!</p>
   
829 
   830 
   831 <h3 id="focus">Element focus</h3>
   
832 
   833 <p>The <code>oncreate</code> pseudo-event is one of the few ways to
   
834 make sure certain dynamic properties such as input focus are handled
   
835 correctly on a page in certain circumstances.</p>
   
836 
   837 <p>This particular example is silly, but it's the sort of real problem
   
838 that crops up in interfaces all the time.</p>
   
839 
   840 <script data-mirror>
   
841 c = make_container();
   
842 
   843 var my_input = null;
   
844 
   845 function input_created(elem){
   
846     my_input = elem;
   
847 }
   
848 function focus_input(){
   
849     my_input.focus();
   
850 }
   
851 
   852 RV.render(c, ['.orange',
   
853     ['input', {oncreate:input_created}],
   
854     ['button', {onclick:focus_input}, 'Focus the input'],
   
855 ]);
   
856 </script>
   
857 
   858 <br><br>
   
859 
   860 <hr>
   
861 
   862 Read more about this library at
   
863 <a href="http://ratfactor.com/retrov/">http://ratfactor.com/retrov/</a>.
   
864 
   865 
   866 
   867 <!-- End of Demo/Tutorial content! -->
   
868 
   869 <script>
   
870 // This helper displays the source of script tags:
   
871 
   872 /*/ The Mirror of Galadriel 2
   
873  *  Copyright 2023 Dave Gauer (ratfactor.com)
   
874  *  Released under the MIT License.
   
875 /*/
   
876 document.addEventListener("DOMContentLoaded", function(e) {
   
877     var scripts = document.querySelectorAll('script[data-mirror]');
   
878     if(scripts.length<1){
   
879         console.log("Galadriel's Mirror 2: No scripts with data-mirror found.");
   
880     }
   
881     scripts.forEach(function(script){
   
882         // remove initial blank line from script (if any)
   
883         var text = script.innerHTML
   
884             .replace(/^\r?\n/, '')
   
885             .replaceAll('<', '<')
   
886             .replaceAll('>', '>');
   
887         // create <pre> to mirror text contents
   
888         var mirror = document.createElement('pre');
   
889         mirror.innerHTML = text;
   
890         script.parentNode.insertBefore(mirror, script);
   
891     });
   
892 });
   
893 </script>
   
894 </body>
   
895 </html>