-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
594 lines (575 loc) · 68.2 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="author" content="Sergio Alcantara (https://github.com/serg-io)" />
<meta name="keywords" content="infuse, HTML, template, templating, engine, template-engine, custom-elements, customized-built-in-elements, custom-tags, web-components, shadow-dom, data-binding, framework, frontend, front-end, DOM, SPA, PWA, webapp, web-apps, web-applications" />
<title>infuse.host - Infuse your HTML with dynamic content.</title>
<link rel="stylesheet" href="docs/dist/style.min.css" />
<style>
a.sourceLine { display: inline-block; line-height: 1.25; }
a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
a.sourceLine:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode { white-space: pre; position: relative; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
code.sourceCode { white-space: pre-wrap; }
a.sourceLine { text-indent: -1em; padding-left: 1em; }
}
pre.numberSource a.sourceLine
{ position: relative; left: -4em; }
pre.numberSource a.sourceLine::before
{ content: attr(title);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; pointer-events: all; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
background-color: #ffffff;
color: #a0a0a0;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #a0a0a0; padding-left: 4px; }
div.sourceCode
{ color: #1f1c1b; background-color: #ffffff; }
@media screen {
a.sourceLine::before { text-decoration: underline; }
}
code span. { color: #1f1c1b; } /* Normal */
code span.al { color: #bf0303; background-color: #f7e6e6; font-weight: bold; } /* Alert */
code span.an { color: #ca60ca; } /* Annotation */
code span.at { color: #0057ae; } /* Attribute */
code span.bn { color: #b08000; } /* BaseN */
code span.bu { color: #644a9b; font-weight: bold; } /* BuiltIn */
code span.cf { color: #1f1c1b; font-weight: bold; } /* ControlFlow */
code span.ch { color: #924c9d; } /* Char */
code span.cn { color: #aa5500; } /* Constant */
code span.co { color: #898887; } /* Comment */
code span.cv { color: #0095ff; } /* CommentVar */
code span.do { color: #607880; } /* Documentation */
code span.dt { color: #0057ae; } /* DataType */
code span.dv { color: #b08000; } /* DecVal */
code span.er { color: #bf0303; text-decoration: underline; } /* Error */
code span.ex { color: #0095ff; font-weight: bold; } /* Extension */
code span.fl { color: #b08000; } /* Float */
code span.fu { color: #644a9b; } /* Function */
code span.im { color: #ff5500; } /* Import */
code span.in { color: #b08000; } /* Information */
code span.kw { color: #1f1c1b; font-weight: bold; } /* Keyword */
code span.op { color: #1f1c1b; } /* Operator */
code span.ot { color: #006e28; } /* Other */
code span.pp { color: #006e28; } /* Preprocessor */
code span.re { color: #0057ae; background-color: #e0e9f8; } /* RegionMarker */
code span.sc { color: #3daee9; } /* SpecialChar */
code span.ss { color: #ff5500; } /* SpecialString */
code span.st { color: #bf0303; } /* String */
code span.va { color: #0057ae; } /* Variable */
code span.vs { color: #bf0303; } /* VerbatimString */
code span.wa { color: #bf0303; } /* Warning */
</style>
</head>
<body>
<button class="side-nav-btn" type="button" onclick="this.nextElementSibling.classList.add('visible')">
<span class="h2">≡</span>
</button>
<div class="side-nav" onclick="this.classList.remove('visible')">
<nav>
<h3>infuse.host</h3>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#webpack">Webpack</a></li>
<li><a href="#examples">Examples</a></li>
<li><a href="#template-literals">Template Literals</a></li>
<li><a href="#expressions">Expressions</a></li>
<li><a href="#custom-constants">Custom Constants</a></li>
<li><a href="#parts">Parts</a><ul>
<li><a href="#attributes">Attributes</a></li>
<li><a href="#boolean-attributes">Boolean Attributes</a></li>
<li><a href="#properties">Properties</a></li>
<li><a href="#text-child-nodes">Text Child Nodes</a></li>
</ul></li>
<li><a href="#event-handlers">Event Handlers</a></li>
<li><a href="#watches">Watches</a></li>
<li><a href="#iterating-templates">Iterating Templates</a></li>
<li><a href="#custom-elements">Custom Elements</a><ul>
<li><a href="#customized-built-in-elements">Customized Built-in Elements</a></li>
</ul></li>
<li><a href="#cleanup">Cleanup</a></li>
<li><a href="#cli">Command Line Interface</a></li>
<li><a href="#configuration-options">Configuration Options</a></li>
<li><a href="#license">License</a></li>
</ul>
<table>
<tr>
<td>
<a href="https://github.com/serg-io/infuse.host" title="GitHub Repository" onclick="event.stopPropagation()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
</svg>
</a>
</td>
<td>Version 0.3.1</td>
<td>
<a href="https://www.npmjs.com/package/infuse.host" target="_blank" title="NPM Package" onclick="event.stopPropagation()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
<path d="M288 288h-32v-64h32v64zm288-128v192H288v32H160v-32H0V160h576zm-416 32H32v128h64v-96h32v96h32V192zm160 0H192v160h64v-32h64V192zm224 0H352v128h64v-96h32v96h32v-96h32v96h32V192z"/>
</svg>
</a>
</td>
</tr>
</table>
</nav>
</div>
<article class="container">
<header id="title-block-header">
<h1 class="display-4">infuse.host</h1>
<p class="lead">Infuse your HTML with dynamic content.</p>
</header>
<section>
<h2 id="introduction">Introduction</h2>
<p>Infuse.host allows you to <strong>infuse</strong> HTML templates with dynamic content. The resulting infused HTML fragments can then be added to <strong>host</strong> elements. This is done by writing <a href="#template-literals">template literals</a> or <a href="#expressions">expressions</a> in your HTML templates. It also allows you to:</p>
<ul>
<li>Write <a href="#event-handlers">event handlers</a>, the same way you would normally write them (using <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers">on-event attributes</a>), but with access to the <code>host</code> and other variables.</li>
<li>Write <a href="#watches">watches</a> to automatically re-infuse an element when an event occurs on another element.</li>
<li>Write <a href="#iterating-templates">iterating templates</a> to infuse a template iteratively, based on values in a given iterable variable.</li>
</ul>
</section>
<section>
<h2 id="installation">Installation</h2>
<p>You can install the <a href="https://www.npmjs.com/package/infuse.host">infuse.host NPM package</a> in your project by running the following command.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb1-1" title="1">$ <span class="ex">npm</span> install infuse.host</a></code></pre></div>
</section>
<section>
<h2 id="webpack">Webpack</h2>
<p>In order to keep things simple, all the code examples shown below have templates that are parsed in the browser (using the <code>parseTemplate</code> function). However, parsing templates in the browser is a practice that is discouraged in a production environment. You can avoid parsing templates in the browser by parsing them as part of a build or back-end process.</p>
<p>If you're using <a href="https://webpack.js.org/">webpack</a>, you can use <a href="https://github.com/serg-io/infuse-loader">infuse-loader</a> to parse templates in HTML files and generate <a href="https://developers.google.com/web/fundamentals/primers/modules#intro">ES modules</a>. These modules can then be imported into other modules. You can install the <a href="https://www.npmjs.com/package/infuse-loader">infuse-loader NPM package</a> in your project by running the following command.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb2-1" title="1">$ <span class="ex">npm</span> install --save-dev infuse-loader</a></code></pre></div>
<p>The site <a href="https://todo.infuse.host/">https://todo.infuse.host/</a> is an example of a web application built using infuse.host, in which all templates are parsed as part of a webpack build process using infuse-loader. The source code can be found at <a href="https://github.com/serg-io/todo.infuse.host">https://github.com/serg-io/todo.infuse.host</a>.</p>
</section>
<section>
<h2 id="examples">Examples</h2>
<p>In the following "Hello World" example, the <code><template></code> element is parsed, cloned, and infused and the resulting fragment (<code><h1>Hello World</h1></code>) is added to <code><header></code> (the <code>host</code> element).</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="500" style="width: 100%;" scrolling="no" title="Hello World - infuse.host" data-src="https://codepen.io/serg-io/embed/YBrwxa/?height=500&theme-id=light&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
<p>The same result can be achieved using a custom element. The <code>Infuse.Host</code> class can be extended to define custom elements. In this example the text is obtained from the custom element (<code>host.title</code> is used in the template instead of <code>data.title</code>).</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="560" style="width: 100%;" scrolling="no" title="Custom "Hello World" element - infuse.host" data-src="https://codepen.io/serg-io/embed/yZzVbX/?height=560&theme-id=light&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
<p>In the previous example the infused fragment is appended to the <code><custom-header></code> using regular DOM. However, a <a href="https://developers.google.com/web/fundamentals/web-components/shadowdom">Shadow DOM</a> can be used by setting the element's <code>shadowRootMode</code> to either <code>'open'</code> or <code>'closed'</code>. In the following example the <code><button></code> element is extended to define a <a href="#customized-built-in-elements">customized built-in element</a> that uses a shadow DOM.</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="550" style="width: 100%;" scrolling="no" title="Customized built-in element with Shadow DOM - infuse.host" data-src="https://codepen.io/serg-io/embed/wNYMMQ/?height=550&theme-id=light&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
<p>The following is a more elaborate example that simulates a shopping cart.</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="550" style="width: 100%;" scrolling="no" title="Shopping Cart - infuse.host" data-src="https://codepen.io/serg-io/embed/vbegWy/?height=550&theme-id=light&default-tab=html,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
</section>
<section>
<h2 id="template-literals">Template Literals</h2>
<p>You can write <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">template literals</a> in your HTML code (inside elements and attribute values), the same way you would write them in regular Javascript code. They start and end with back-tick characters, they can contain strings and expressions, and they can be tagged. However, in order to be able to tag template literals, you must tell infuse.host what are the tag functions that you use within your HTML templates. You do this by setting the <code>tags</code> <a href="#configuration-options">configuration option</a>.</p>
<p>A common use case for tagged template literals is internationalization. For instance, if you want to tag your template literals with a tag function called <code>i18n</code> you must use the configs ES module to set the <code>tags</code> configuration option:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb3-1" title="1"><span class="im">import</span> <span class="op">{</span> setConfigs <span class="op">}</span> <span class="im">from</span> <span class="st">'path/to/infuse.host/src/configs.js'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb3-2" title="2"></a>
<a class="sourceLine" id="cb3-3" title="3"><span class="kw">const</span> dictionary <span class="op">=</span> <span class="op">{</span> <span class="dt">submit</span><span class="op">:</span> <span class="st">'Submit'</span> <span class="op">};</span></a>
<a class="sourceLine" id="cb3-4" title="4"></a>
<a class="sourceLine" id="cb3-5" title="5"><span class="co">/**</span></a>
<a class="sourceLine" id="cb3-6" title="6"><span class="co"> * Set the "tags" configuration option. Must be set before parsing.</span></a>
<a class="sourceLine" id="cb3-7" title="7"><span class="co"> * The `i18n` tag function retrieves the value of the given `key` from the `dictionary`.</span></a>
<a class="sourceLine" id="cb3-8" title="8"><span class="co"> */</span></a>
<a class="sourceLine" id="cb3-9" title="9"><span class="at">setConfigs</span>(<span class="op">{</span></a>
<a class="sourceLine" id="cb3-10" title="10"> <span class="dt">tags</span><span class="op">:</span> <span class="op">{</span></a>
<a class="sourceLine" id="cb3-11" title="11"> <span class="dt">i18n</span><span class="op">:</span> <span class="kw">function</span>([key]) <span class="op">{</span></a>
<a class="sourceLine" id="cb3-12" title="12"> <span class="cf">return</span> dictionary[key]<span class="op">;</span></a>
<a class="sourceLine" id="cb3-13" title="13"> <span class="op">}</span></a>
<a class="sourceLine" id="cb3-14" title="14"> <span class="op">}</span></a>
<a class="sourceLine" id="cb3-15" title="15"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>The parser uses the "tags" configuration option to identify the tag functions that you use in your HTML templates. For instance, if you use the <code>tags</code> object shown above, <code>i18n`submit`</code> will be infused with (replaced with) the string "Submit".</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="500" style="width: 100%;" scrolling="no" title="Tagged template literal - infuse.host" data-src="https://codepen.io/serg-io/embed/NoXRjB/?height=500&theme-id=light&default-tab=html,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
</section>
<section>
<h2 id="expressions">Expressions</h2>
<p>An expression starts with <code>${</code> and ends with <code>}</code> and contains Javascript code, which is evaluated during the infuse process, and its result is used to infuse the corresponding part of an element. Expressions can be used inside template literals, inside back-tick characters (just like in regular Javascript code), or directly in your HTML code, without back-tick characters.</p>
<p>The following variables are available within expressions:</p>
<ul>
<li><strong><code>this</code></strong>: The element that contains the expression.</li>
<li><strong><code>host</code></strong>: The element to which the infused fragment will be added.</li>
<li><strong><code>data</code></strong>: An optional data object.</li>
<li><strong><code>tags</code></strong>: The configuration object that contains all tag functions.</li>
<li><strong><code>event</code></strong>: When an element is infused by a <a href="#watches">watch</a>, the <code>event</code> variable is the event that triggered the infusion. This variable is <code>undefined</code> the first time an element is infused.</li>
<li>Elements can define their own <a href="#custom-constants">custom constant</a> variables.</li>
<li>Additional <strong>iteration constant</strong> variables are available within <a href="#iterating-templates">iterating templates</a>.</li>
</ul>
<p>Expressions and template literals can be used in combination with static strings. For instance:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb4-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> class=</span><span class="st">"btn btn-${ data.hasWarnings ? 'warning' : 'success' }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb4-2" title="2"> i18n`submit`</a>
<a class="sourceLine" id="cb4-3" title="3"><span class="kw"></button></span></a></code></pre></div>
</section>
<section>
<h2 id="custom-constants">Custom Constants</h2>
<p>Custom constants are variables available within the expressions and event handlers of an element. These constants are defined using attributes that follow this format: <code>const-variable-name="${ expression }"</code>, where <code>variable-name</code> is the name of the variable, <code>expression</code> is the Javascript code that gets evaluated (right before the element is infused for the first time), and the resulting value is assigned to the variable.</p>
<p>For instance, lets say that <code>host.getCarInventory()</code> returns an array of objects, each object represents a car with a <code>color</code> attribute. The following paragraph displays how many blue cars there are in the inventory:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb5-1" title="1"><span class="kw"><p</span><span class="ot"> const-car-inventory=</span><span class="st">"${ host.getCarInventory() }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb5-2" title="2"> Number of blue cars: ${ carInventory.filter(car => car.color === 'blue').length }</a>
<a class="sourceLine" id="cb5-3" title="3"><span class="kw"></p></span></a></code></pre></div>
<p><strong>Note</strong>: In HTML, attributes names are case-insensitive (they're <a href="https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-attributes">lower-cased automatically</a>). To define a variable with uppercase letters in its name, you must use dashes as shown in the previous example.</p>
<p><strong>Note</strong>: the <code>event</code> variable is not available in expressions to declare custom constants.</p>
</section>
<section>
<h2 id="parts">Parts</h2>
<p>There are four parts of an element that can be infused:</p>
<ul>
<li>Attributes</li>
<li>Boolean attributes</li>
<li>Properties</li>
<li>Text child nodes</li>
</ul>
<section>
<h3 id="attributes">Attributes</h3>
<p>In the following example, the <code>class</code> attribute will be infused with "is-valid" (in addition to "form-control") if <code>host.isEmailValid</code> evaluates to a truthy value:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb6-1" title="1"><span class="kw"><input</span><span class="ot"> type=</span><span class="st">"email"</span><span class="ot"> class=</span><span class="st">"form-control ${ host.isEmailValid ? 'is-valid' : '' }"</span><span class="kw">></span></a></code></pre></div>
</section>
<section>
<h3 id="boolean-attributes">Boolean Attributes</h3>
<p>Boolean attributes have names that end with a question mark and they're added to the element if their expressions result in a <a href="https://developer.mozilla.org/en-US/docs/Glossary/Truthy">truthy value</a>.</p>
<p>If the result of an expression is <code>true</code>, the attribute is added to the element using an empty string as its value. If the result is a truthy value, other than <code>true</code>, it will be used as the value of the attribute. Otherwise, if the expression results in a <a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">falsy value</a>, the attribute is removed from the element.</p>
<p>In the following example, the attribute <code>disabled</code> is added to the <code><button></code> element if <code>host.isFormInvalid</code> is <code>true</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb7-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ host.isFormInvalid }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb7-2" title="2"> Submit</a>
<a class="sourceLine" id="cb7-3" title="3"><span class="kw"></button></span></a></code></pre></div>
<p>When an expressions results in a string that is <strong>not empty</strong>, the string will be used as the value of the attribute. For instance, if <code>host.getBtnClass()</code> returns the string <code>"btn btn-warning"</code>, it will be used as the value of the <code>class</code> attribute. However, if it returns a falsy value, the <code>class</code> attribute will not be added to the button at all.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb8-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"reset"</span><span class="ot"> class</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ host.getBtnClass() }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb8-2" title="2"> Clear Form</a>
<a class="sourceLine" id="cb8-3" title="3"><span class="kw"></button></span></a></code></pre></div>
</section>
<section>
<h3 id="properties">Properties</h3>
<p>Properties of elements can be infused by adding an attribute in the HTML code that starts with a dot. The expression given in the value will be evaluated and the result will be assigned to the specified <strong>property</strong> of the element. In the following example, the expression evaluates to a <code>Date</code> instance which is assigned to the property <code>valueAsDate</code> of the input element:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb9-1" title="1"><span class="kw"><input</span><span class="ot"> type=</span><span class="st">"date"</span><span class="ot"> name=</span><span class="st">"moon-landing"</span> <span class="er">.value-as-date</span><span class="ot">=</span><span class="st">"${ new Date(1969, 6, 20) }"</span><span class="kw">></span></a></code></pre></div>
<p><strong>Note</strong>: In HTML, attributes names are case-insensitive (they're <a href="https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-attributes">lower-cased automatically</a>). To infuse a property with uppercase letters in its name use dashes as shown in the example above.</p>
</section>
<section>
<h3 id="text-child-nodes">Text Child Nodes</h3>
<p>The <strong>text</strong> child nodes of an element can be infused by adding an expression or a template literal:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb10-1" title="1"><span class="kw"><p></span>${ host.paragraphText }<span class="kw"></p></span></a></code></pre></div>
<p>Tagged template literals can be used for things like internationalization:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb11-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ host.isFormInvalid }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb11-2" title="2"> i18n`submit`</a>
<a class="sourceLine" id="cb11-3" title="3"><span class="kw"></button></span></a></code></pre></div>
</section>
</section>
<section>
<h2 id="event-handlers">Event Handlers</h2>
<p>You can add event handlers to elements, just like you would in regular HTML code, by using <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers">on-event attributes</a>.</p>
<p>When an element is parsed, event handler attributes are converted into event listener functions (<a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers#Terminology">terminology</a>). When an element is infused for the first time, these event listeners are added to the element using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"><code>addEventListener</code></a> method.</p>
<p>Event handlers have access to the same variables available within expressions. For instance, if you want to call a method on the <code>host</code> element when a form is submitted, you would add an <code>onsubmit</code> event handler attribute.</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="500" style="width: 100%;" scrolling="no" title="Event handler - infuse.host" data-src="https://codepen.io/serg-io/embed/KJQgPQ/?height=500&theme-id=light&default-tab=html,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
</section>
<section>
<h2 id="watches">Watches</h2>
<p>Watches re-infuse an element when an event occurs on itself or another element.</p>
<p>For instance, when the following button is infused for the first time the <code>disabled</code> boolean attribute will not be added because the <code>event</code> variable is <code>undefined</code>.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb12-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb12-2" title="2"> i18n`submit`</a>
<a class="sourceLine" id="cb12-3" title="3"><span class="kw"></button></span></a></code></pre></div>
<p>A watch can be added to infuse the button again whenever it is clicked. When a watch infuses an element, the value of <code>event</code> is the event that triggered the infusion.</p>
<p>In the following example, the watch will listen for the <code>click</code> event on the button itself (<code>this</code>). When the event occurs, the element will be re-infused, this time the <code>event</code> will be the <code>click</code> event that triggered the watch, which will cause the <code>disabled</code> attribute to be added to the button.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb13-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-this=</span><span class="st">"click"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb13-2" title="2"> i18n`submit`</a>
<a class="sourceLine" id="cb13-3" title="3"><span class="kw"></button></span></a></code></pre></div>
<p>Watches can also be used to watch events on other elements. For instance, the button can be infused when a <code>submit</code> event occurs on the <code>host</code> element:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb14-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"submit"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb14-2" title="2"> i18n`submit`</a>
<a class="sourceLine" id="cb14-3" title="3"><span class="kw"></button></span></a></code></pre></div>
<p>If the <code>host</code> element in the previous example contains multiple forms, the button will be disabled whenever any of those forms are submitted. This is because events <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture">bubble up the DOM</a> and eventually reach the <code>host</code>.</p>
<p>Watches can be limited to infuse an element only when the specified type of event is triggered by an element that matches a given selector. For instance, the following button will be disabled when a <code>submit</code> event reaches the <code>host</code> element and the event was triggered by a form with "login-form" as its ID:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb15-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"submit #login-form"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb15-2" title="2"> i18n`login`</a>
<a class="sourceLine" id="cb15-3" title="3"><span class="kw"></button></span></a></code></pre></div>
<p>Watches are written in this format: <code>watch-variable="event-map"</code>, where <code>variable</code> is the name of the variable (element) to watch. This is usually <code>host</code> or <code>this</code>, but it can be any variable available within the element's expressions as long as it's an <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element"><code>Element</code></a> or implements the <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"><code>addEventListener</code></a> method.</p>
<p>The <code>event-map</code> can be written in any of the following formats:</p>
<ul>
<li>A string, <code>watch-variable="eventList"</code>, where:
<ul>
<li><code>eventList</code> is a list of semicolon-separated <code>event</code>s: <code>event1[; event2]...[; eventN]</code>.</li>
<li><code>event</code> is an <code>eventType</code> optionally followed by a comma-separated list of <code>selector</code>s: <code>eventType[ selector1[, selector2]...[, selectorN] ]</code></li>
<li><code>eventType</code> is the type of event to watch, for instance <code>click</code> or <code>submit</code>.</li>
<li><code>selector</code> is an optional CSS selector used for event delegation. A watch will only infuse an element if the element that triggered the event matches the <code>selector</code>.</li>
</ul>
<p>For instance, the following button will be infused only when a form triggers a <code>submit</code> event or a form field with the <code>required</code> boolean attribute triggers an <code>invalid</code> event:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb16-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"submit; invalid [required]"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb16-2" title="2"> i18n`signup`</a>
<a class="sourceLine" id="cb16-3" title="3"><span class="kw"></button></span></a></code></pre></div></li>
<li>An object, <code>watch-variable="{ eventList: parts }"</code>, where:
<ul>
<li>The keys follow the <code>eventList</code> structure explained above.</li>
<li>The values indicate one (a string or a number) or multiple (an array) parts of the element to infuse.
<ul>
<li>If the part is a boolean attribute, it must end with a question mark (i.e. <code>disabled?</code>).</li>
<li>If the part is a property, it must start with a dot and it can be either dashed (i.e. <code>.value-as-date</code>) or camel cased (i.e. <code>.valueAsDate</code>).</li>
<li><p>If the part is a text child node, it must be a number indicating the zero-based index position of the text child node to infuse. For instance, consider the following <code><p></code> element:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb17-1" title="1"><span class="kw"><p></span>i18n`total`<span class="kw"><strong></span>=<span class="kw"></strong></span>${ host.getTotal() }<span class="kw"></p></span></a></code></pre></div>
It contains three child nodes:
<ol start="0" type="1">
<li>A text child node (<code>i18n`submit`</code>).</li>
<li>An element child node (<code><strong>=</strong></code>).</li>
<li>A text child node (<code>${ host.getTotal() }</code>).</li>
</ol></li>
</ul></li>
</ul>
<p>In the following example, when a <code>submit</code> event reaches the <code>host</code>, the watch will <strong>only</strong> infuse the <code>disabled</code> boolean attribute.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb18-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"{ submit: 'disabled?' }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb18-2" title="2"> i18n`signup`</a>
<a class="sourceLine" id="cb18-3" title="3"><span class="kw"></button></span></a></code></pre></div></li>
<li><p>An array of arrays, <code>watch-variable="[ [eventList, parts] ]"</code>. This is the same as using an object, but the <code>eventList</code>/<code>parts</code> pairs are written as arrays. For instance, the watch in the previous example can be written as an array of arrays and the watch would work the same way.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb19-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"[ ['submit', 'disabled?'] ]"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb19-2" title="2"> i18n`signup`</a>
<a class="sourceLine" id="cb19-3" title="3"><span class="kw"></button></span></a></code></pre></div></li>
<li><p>An expression, <code>watch-variable="${ expression }"</code>. The expression, when evaluated, must return a value in one of the formats listed above (a string, an object, or an array of arrays).</p>
<p>In the following example, <code>host.getEventMapForSubmitBtn()</code> must return a string, an object, or an array of arrays:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb20-1" title="1"><span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ event !== undefined }"</span><span class="ot"> watch-host=</span><span class="st">"${ host.getEventMapForSubmitBtn() }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb20-2" title="2"> i18n`signup`</a>
<a class="sourceLine" id="cb20-3" title="3"><span class="kw"></button></span></a></code></pre></div></li>
</ul>
</section>
<section>
<h2 id="iterating-templates">Iterating Templates</h2>
<p>Iterating templates use the <code>for</code> and <code>each</code> attributes to indicate that the template must be cloned and infused multiple times. The value of the <code>for</code> attribute is a string with up to 3 comma-separated variable names to use in each iteration. The value of the <code>each</code> attribute must be an expression. Evaluating the expression must result in a collection (array, <code>Map</code>, <code>Set</code>, or any value that has a <code>forEach</code> method). The collection is used to clone the template multiple times, once for each value in the collection.</p>
<p>Using the <code>for</code> and <code>each</code> attributes to clone and infuse a template multiple times is analogous to using the <code>forEach</code> method to iterate over multiple values in regular Javascript. For instance, in Javascript you would write a <code>forEach</code> loop like this:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb21-1" title="1"><span class="va">host</span>.<span class="at">getBooksArray</span>().<span class="at">forEach</span>(<span class="kw">function</span>(book<span class="op">,</span> index<span class="op">,</span> books) <span class="op">{</span></a>
<a class="sourceLine" id="cb21-2" title="2"> <span class="co">// Code to execute in each iteration.</span></a>
<a class="sourceLine" id="cb21-3" title="3"> <span class="co">// The variables `book`, `index`, and `books` are available within each iteration.</span></a>
<a class="sourceLine" id="cb21-4" title="4"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>A similar iteration can be implemented to clone and infuse a template multiple times by adding the <code>for</code> and <code>each</code> attributes to the <code><template></code> element:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb22-1" title="1"><span class="kw"><template</span><span class="ot"> for=</span><span class="st">"book, index, books"</span><span class="ot"> each=</span><span class="st">"${ host.getBooksArray() }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb22-2" title="2"> <span class="co"><!--</span></a>
<a class="sourceLine" id="cb22-3" title="3"><span class="co"> Contents of this template will be cloned and infuse once for each `book`.</span></a>
<a class="sourceLine" id="cb22-4" title="4"><span class="co"> The variables `book`, `index`, and `books` are available within any expression and</span></a>
<a class="sourceLine" id="cb22-5" title="5"><span class="co"> event handlers inside this template.</span></a>
<a class="sourceLine" id="cb22-6" title="6"><span class="co"> --></span></a>
<a class="sourceLine" id="cb22-7" title="7"><span class="kw"></template></span></a></code></pre></div>
<p>Lets say that <code>host.getBooksArray()</code> returns an array of objects, each object represents a book and contains the attributes <code>isbn</code>, <code>title</code>, and <code>author</code>. The following template can be used to generate a <code><table></code> with a list of books.</p>
<p class="loading-iframe">
Loading example...
</p>
<iframe height="550" style="width: 100%;" scrolling="no" title="Iterating template - infuse.host" data-src="https://codepen.io/serg-io/embed/KJQzmP/?height=550&theme-id=light&default-tab=html,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
</iframe>
<p>Note how the iterating template is inside a parent template. When a template is cloned and infused, nested templates are also cloned and infused.</p>
</section>
<section>
<h2 id="custom-elements">Custom Elements</h2>
<p>The <code>Infuse.Host</code> class can be extended to define a class for a custom element.</p>
<p>Defining a <code>template</code> property is the only requirement when extending <code>Infuse.Host</code>. The purpose of the <code>template</code> property is to provide the template element that will be cloned and infused whenever the custom element is used.</p>
<p>By default, the infused fragment will be appended to the custom element's <a href="https://developers.google.com/web/fundamentals/web-components/shadowdom#lightdom">Light DOM</a>. However, a <a href="https://developers.google.com/web/fundamentals/web-components/shadowdom">Shadow DOM</a> can be used by setting the <code>shadowRootMode</code> property to either <code>'open'</code> or <code>'close'</code> (which are the two modes that the <a href="https://developer.mozilla.org/en-US/docs/Web/API/element/attachShadow"><code>attachShadow</code></a> method accepts).</p>
<p>Both, the <code>template</code> and <code>shadowRootMode</code> properties, can be defined as getters or regular functions.</p>
<p>Lets say you want to define a custom element for a login form, that uses a Shadow DOM, and that you're using webpack and <a href="https://github.com/serg-io/infuse-loader">infuse-loader</a> to parse and import your templates. The following code would define a custom element called <code><login-form></code>.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb23-1" title="1"><span class="im">import</span> <span class="op">*</span> <span class="im">as</span> Infuse <span class="im">from</span> <span class="st">'path/to/infuse.host/src/infuse.js'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb23-2" title="2"><span class="im">import</span> loginTemplate <span class="im">from</span> <span class="st">'./login-form.html'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb23-3" title="3"></a>
<a class="sourceLine" id="cb23-4" title="4"><span class="co">// Extend `Infuse.Host` to define a class for the new custom element.</span></a>
<a class="sourceLine" id="cb23-5" title="5"><span class="kw">class</span> LoginForm <span class="kw">extends</span> <span class="va">Infuse</span>.<span class="at">Host</span> <span class="op">{</span></a>
<a class="sourceLine" id="cb23-6" title="6"> get <span class="at">template</span>() <span class="op">{</span></a>
<a class="sourceLine" id="cb23-7" title="7"> <span class="co">// Return the parsed template.</span></a>
<a class="sourceLine" id="cb23-8" title="8"> <span class="cf">return</span> loginTemplate<span class="op">;</span></a>
<a class="sourceLine" id="cb23-9" title="9"> <span class="op">}</span></a>
<a class="sourceLine" id="cb23-10" title="10"></a>
<a class="sourceLine" id="cb23-11" title="11"> get <span class="at">shadowRootMode</span>() <span class="op">{</span></a>
<a class="sourceLine" id="cb23-12" title="12"> <span class="co">// Use an "open" shadow root.</span></a>
<a class="sourceLine" id="cb23-13" title="13"> <span class="cf">return</span> <span class="st">'open'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb23-14" title="14"> <span class="op">}</span></a>
<a class="sourceLine" id="cb23-15" title="15"><span class="op">}</span></a>
<a class="sourceLine" id="cb23-16" title="16"></a>
<a class="sourceLine" id="cb23-17" title="17"><span class="co">// Define the custom element.</span></a>
<a class="sourceLine" id="cb23-18" title="18"><span class="va">window</span>.<span class="va">customElements</span>.<span class="at">define</span>(<span class="st">'login-form'</span><span class="op">,</span> LoginForm)<span class="op">;</span></a></code></pre></div>
<p>When the <code><login-form></code> custom element is used, <code>Infuse.Host</code> uses the <code>template</code> property to obtain the template that needs to be cloned and infused. And, since <code>shadowRootMode</code> is set to <code>'open'</code>, the resulting fragment is added to the custom element's Shadow DOM. When the element is removed from the DOM, memory allocated for the element, and any of its descendants, is cleared automatically, there's no need to call the <code>clear</code> function.</p>
<section>
<h3 id="customized-built-in-elements">Customized Built-in Elements</h3>
<p>If you want to extend one of the browser's built-in elements you can use the <code>CustomHost</code> function to define a <a href="https://developers.google.com/web/fundamentals/web-components/customelements#extendhtml">customized built-in element</a>.</p>
<p>For instance, lets say that your application contains a shopping cart and you want to summarize the items in the cart using a list (an <code><ul></code> element) where each element in the list is an item in the cart. You can define a <strong>customized built-in element</strong> that extends the <code>HTMLLIElement</code> class (the <code><li></code> element):</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb24-1" title="1"><span class="im">import</span> <span class="op">{</span> CustomHost <span class="op">}</span> <span class="im">from</span> <span class="st">'path/to/infuse.host/src/infuse.js'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb24-2" title="2"><span class="im">import</span> cartItemTemplate <span class="im">from</span> <span class="st">'./cart-item.html'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb24-3" title="3"></a>
<a class="sourceLine" id="cb24-4" title="4"><span class="co">/**</span></a>
<a class="sourceLine" id="cb24-5" title="5"><span class="co"> * Extend the `HTMLLIElement` class to define a new `CartItem` class.</span></a>
<a class="sourceLine" id="cb24-6" title="6"><span class="co"> */</span></a>
<a class="sourceLine" id="cb24-7" title="7"><span class="kw">class</span> CartItem <span class="kw">extends</span> <span class="at">CustomHost</span>(HTMLLIElement) <span class="op">{</span></a>
<a class="sourceLine" id="cb24-8" title="8"> get <span class="at">template</span>() <span class="op">{</span></a>
<a class="sourceLine" id="cb24-9" title="9"> <span class="co">// Return the parsed template.</span></a>
<a class="sourceLine" id="cb24-10" title="10"> <span class="cf">return</span> cartItemTemplate<span class="op">;</span></a>
<a class="sourceLine" id="cb24-11" title="11"> <span class="op">}</span></a>
<a class="sourceLine" id="cb24-12" title="12"><span class="op">}</span></a>
<a class="sourceLine" id="cb24-13" title="13"></a>
<a class="sourceLine" id="cb24-14" title="14"><span class="co">/**</span></a>
<a class="sourceLine" id="cb24-15" title="15"><span class="co"> * When defining customized built-in elements, you must specify (in the third argument) what</span></a>
<a class="sourceLine" id="cb24-16" title="16"><span class="co"> * built-in HTML tag the custom element extends.</span></a>
<a class="sourceLine" id="cb24-17" title="17"><span class="co"> */</span></a>
<a class="sourceLine" id="cb24-18" title="18"><span class="va">window</span>.<span class="va">customElements</span>.<span class="at">define</span>(<span class="st">'cart-item'</span><span class="op">,</span> CartItem<span class="op">,</span> <span class="op">{</span> <span class="dt">extends</span><span class="op">:</span> <span class="st">'li'</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>Once defined, you can create them manually using the constructor:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb25-1" title="1"><span class="kw">const</span> item <span class="op">=</span> <span class="kw">new</span> <span class="at">CartItem</span>()<span class="op">;</span></a></code></pre></div>
<p>or using <code>document.createElement</code>:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb26-1" title="1"><span class="kw">const</span> item <span class="op">=</span> <span class="va">document</span>.<span class="at">createElement</span>(<span class="st">'li'</span><span class="op">,</span> <span class="op">{</span> <span class="dt">is</span><span class="op">:</span> <span class="st">'cart-item'</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>and add them to the DOM manually.</p>
<p>You can also use them in HTML templates:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb27-1" title="1"><span class="kw"><ul></span></a>
<a class="sourceLine" id="cb27-2" title="2"> <span class="kw"><template</span><span class="ot"> for=</span><span class="st">"item"</span><span class="ot"> each=</span><span class="st">"${ host.shoppingCartItems }"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb27-3" title="3"> <span class="kw"><li</span><span class="ot"> is=</span><span class="st">"cart-item"</span> <span class="er">.description</span><span class="ot">=</span><span class="st">"${ item.description }"</span> <span class="er">.price</span><span class="ot">=</span><span class="st">"${ item.price }"</span><span class="kw">></li></span></a>
<a class="sourceLine" id="cb27-4" title="4"> <span class="kw"></template></span></a>
<a class="sourceLine" id="cb27-5" title="5"><span class="kw"></ul></span></a></code></pre></div>
</section>
</section>
<section>
<h2 id="cleanup">Cleanup</h2>
<p>When a template is cloned and the cloned fragment is infused, memory is allocated to create variables, event listeners, and watches. Infuse.host keeps track of allocated memory. When an element is removed from the DOM and is no longer needed, memory associated with that element must be cleared. This memory can be cleared using the <code>clear</code> function. The <code>clear</code> function searches for infused elements and clears memory associated with the elements it finds.</p>
<p>In the following example a <code><form></code> element is removed from the DOM and the <code>clear</code> function is called using the <code>form</code> as the only argument. The <code>clear</code> function will search for infused elements inside the <code>form</code> and clear memory that was allocated to infuse them. If the form itself is an infused element, memory associated with the form will also be cleared.</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb28-1" title="1"><span class="im">import</span> <span class="op">{</span> clear <span class="op">}</span> <span class="im">from</span> <span class="st">'path/to/infuse.host/src/infuse.js'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb28-2" title="2"></a>
<a class="sourceLine" id="cb28-3" title="3"><span class="kw">const</span> form <span class="op">=</span> <span class="va">document</span>.<span class="at">querySelector</span>(<span class="st">'#login-form'</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb28-4" title="4"></a>
<a class="sourceLine" id="cb28-5" title="5"><span class="co">// Remove form from the DOM.</span></a>
<a class="sourceLine" id="cb28-6" title="6"><span class="va">form</span>.<span class="at">remove</span>()<span class="op">;</span></a>
<a class="sourceLine" id="cb28-7" title="7"></a>
<a class="sourceLine" id="cb28-8" title="8"><span class="co">// Clear memory from any infused element within the form (including the form itself).</span></a>
<a class="sourceLine" id="cb28-9" title="9"><span class="at">clear</span>(form)<span class="op">;</span></a></code></pre></div>
<p><strong>Note:</strong> Custom elements defined using the <code>Infuse.Host</code> class or the <code>Infuse.CustomHost</code> function call the <code>clear</code> method when the custom element is removed from the DOM (when the <code>disconnectedCallback</code> method is called). Therefore, the <code>clear</code> function doesn't need to be called for custom elements defined using <code>Infuse.Host</code> or <code>Infuse.CustomHost</code>.</p>
</section>
<section>
<h2 id="cli">Command Line Interface</h2>
<p>The <a href="https://www.npmjs.com/package/infuse-cli">infuse-cli</a> package provides a command line interface (CLI) to convert HTML templates (files) to ES Modules which can be used with infuse.host. To install it execute the following command:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb29-1" title="1">$ <span class="ex">npm</span> install infuse-cli</a></code></pre></div>
<p>Once installed you can use the <code>infuse</code> command in your <a href="https://docs.npmjs.com/misc/scripts">NPM scripts</a> or by using it with NPM's <code>npx</code> command. For instance, the following command will parse templates in the <em>src/template.html</em> file and will write the generated ES Module to <em>dist/template.js</em>.</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb30-1" title="1">$ <span class="ex">npx</span> infuse src/template.html dist/template.js</a></code></pre></div>
<p>Documentation and additional examples can be found in the <a href="https://www.npmjs.com/package/infuse-cli">infuse-cli</a> package web page. You can also use the CLI to access the documentation:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb31-1" title="1">$ <span class="ex">npx</span> infuse --help</a></code></pre></div>
<h2 id="configuration-options">Configuration Options</h2>
<p>The config ES module allows you to change configuration options.</p>
<p>In the following example, the <code>setConfigs</code> function is used to change the configuration options <code>eventHandlerExp</code> and <code>eventName</code>:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb32-1" title="1"><span class="im">import</span> <span class="op">{</span> setConfigs <span class="op">}</span> <span class="im">from</span> <span class="st">'path/to/infuse.host/src/configs.js'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb32-2" title="2"></a>
<a class="sourceLine" id="cb32-3" title="3"><span class="at">setConfigs</span>(<span class="op">{</span></a>
<a class="sourceLine" id="cb32-4" title="4"> <span class="co">// Change the name of the "event" variable to just "e".</span></a>
<a class="sourceLine" id="cb32-5" title="5"> <span class="dt">eventName</span><span class="op">:</span> <span class="st">'e'</span><span class="op">,</span></a>
<a class="sourceLine" id="cb32-6" title="6"> <span class="co">// Change the prefix of event handler attributes to "on-".</span></a>
<a class="sourceLine" id="cb32-7" title="7"> <span class="dt">eventHandlerExp</span><span class="op">:</span> <span class="st">'on-'</span></a>
<a class="sourceLine" id="cb32-8" title="8"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>The following is a list of all the configuration options:</p>
<ul>
<li><p><strong><code>camelCaseEvents</code></strong>: Indicates whether or not event names in event handlers should be camel cased during the parsing process. This option is <code>false</code> by default.</p>
<p>Consider the following element:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb33-1" title="1"><span class="kw"><form</span><span class="ot"> on-client-side-validation=</span><span class="st">"host.enableSubmitBtn(event)"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb33-2" title="2"> <span class="co"><!-- Form fields. --></span></a>
<a class="sourceLine" id="cb33-3" title="3"><span class="kw"></form></span></a></code></pre></div>
<p>By default, this event handler will be listening for <code>'client-side-validation'</code> events. However, if <code>camelCaseEvents</code> is set to <code>true</code>, the event handler would be listening for <code>'clientSideValidation'</code> events instead.</p></li>
<li><p><strong><code>constantExp</code></strong>: The prefix (a string) or a regular expression used to determine if an attribute is a custom constant. This is <code>'const-'</code> by default which means that all attributes that start with <code>const-</code> are custom constant definitions.</p>
<p>If <code>constantExp</code> is set to a regular expression, the regular expression is used to determine if attributes are custom constants. For instance, if you want custom constant attribute names to end with <code>-const</code> you would use the following regular expression:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb34-1" title="1"><span class="at">setConfigs</span>(<span class="op">{</span> <span class="dt">constantExp</span><span class="op">:</span> <span class="ss">/</span><span class="sc">^(\w+)</span><span class="ss">-const</span><span class="sc">$</span><span class="ss">/</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>If you're using a regular expression, it must contain parenthesis, as shown above. The parenthesis indicate the location of the name of the variable.</p></li>
<li><p><strong><code>contextFunctionId</code></strong>: When an element is parsed a context function is generated. An ID that uniquely identifies the context function is also created. This ID is added to the element as an attribute. The <code>contextFunctionId</code> option defines the name of that attribute. This is <code>'data-cid'</code> by default.</p></li>
<li><p><strong><code>eventHandlerExp</code></strong>: The prefix (a string) or a regular expression used to determine if an attribute is an event handler. By default, event handler attributes start with <code>on</code> or <code>on-</code> and can contain alphanumeric characters, underscores, dashes, and colons. The following regular expression is used by default to identify event handler attributes:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb35-1" title="1"><span class="ss">/</span><span class="sc">^</span><span class="ss">on-</span><span class="sc">?(\w[\w:-]+)$</span><span class="ss">/</span></a></code></pre></div>
<p>Parentheses are required when using a regular expression, they indicate the location of the event type. For instance, when using the following expression:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb36-1" title="1"><span class="at">setConfigs</span>(<span class="op">{</span> <span class="dt">eventHandlerExp</span><span class="op">:</span> <span class="ss">/</span><span class="sc">^</span><span class="ss">when-</span><span class="sc">(\w+)</span><span class="ss">-run</span><span class="sc">$</span><span class="ss">/</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>you would write event handlers using the format <code>when-eventtype-run="expression"</code>, where <code>eventtype</code> is the type of event and <code>expression</code> is the expression to execute when the event occurs. For example, when using <code>when-submit-run="event.preventDefault()"</code> the event handler would listen for <code>submit</code> events.</p>
<p>Alternatively, you can use a string to indicate the prefix of event handlers. For instance, when using <code>'on'</code>:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb37-1" title="1"><span class="at">setConfigs</span>(<span class="op">{</span> <span class="dt">eventHandlerExp</span><span class="op">:</span> <span class="st">'on'</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>all attributes that start with <code>on</code> will be treated as event handlers.</p></li>
<li><p><strong><code>eventName</code></strong>: Name of the event variable available within expressions and event handlers. This is <code>'event'</code> by default. If you wanted event variables to be named <code>e</code> instead, you would have to change this configuration option:</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb38-1" title="1"><span class="at">setConfigs</span>(<span class="op">{</span> <span class="dt">eventName</span><span class="op">:</span> <span class="st">'e'</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>This would allow you to use <code>e</code> (instead of <code>event</code>) to access event variables within expressions and event handlers. For instance:</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb39-1" title="1"><span class="kw"><form</span><span class="ot"> onsubmit=</span><span class="st">"e.preventDefault()"</span><span class="kw">></span></a>
<a class="sourceLine" id="cb39-2" title="2"> <span class="kw"><button</span><span class="ot"> type=</span><span class="st">"submit"</span><span class="ot"> disabled</span><span class="er">?</span><span class="ot">=</span><span class="st">"${ e !== undefined }"</span><span class="kw">></span>Submit<span class="kw"></button></span></a>
<a class="sourceLine" id="cb39-3" title="3"><span class="kw"></form></span></a></code></pre></div></li>
<li><p><strong><code>placeholderId</code></strong>: Nested templates are replaced with placeholder templates during the parsing process. Placeholder templates have an attribute that uniquely identifies the template that was replaced. The <code>placeholderId</code> defines the name of the attribute. This is <code>'data-pid'</code> by default.</p></li>
<li><p><strong><code>sweepFlag</code></strong>: Name of the boolean attribute to use as an indicator that memory has been allocated for an element. Allocated memory must be cleared when the element is removed from the DOM. This is <code>'data-sweep'</code> by default.</p></li>
<li><p><strong><code>tags</code></strong>: An object containing all possible tag functions to be used with template literals. During parsing (for instance, when using the <a href="https://github.com/serg-io/infuse-loader">infuse-loader</a>) this can be an array (of strings) of all possible tag function names. However, at run time, this must be an object where the keys are the names of the tag functions and the values are the tag functions.</p></li>
<li><p><strong><code>tagsName</code></strong>: Name of the variable, available within expressions and event handlers, that contains all tag functions. This is <code>'tags'</code> by default.</p></li>
<li><p><strong><code>templateId</code></strong>: Name of the template ID attribute. After parsing a template, a template ID attribute is automatically generated and added to the template element. This configuration option is the name of that attribute and it's <code>'data-tid'</code> by default.</p>
<p>If the template element already has this attribute when it's parsed, a new ID is <strong>not</strong> generated, instead the value of the attribute is used to identify the template.</p>
<p>If you're already setting the <code>id</code> attribute on your templates, changing this configuration option to <code>id</code> would make it easier to access your templates (especially if you're using infuse-loader, see <a href="https://github.com/serg-io/infuse-loader">infuse-loader</a> for details).</p></li>
<li><p><strong><code>watchExp</code></strong>: The prefix (a string) or a regular expression used to determine if an attribute is a watch. This is <code>'watch-'</code> by default which means that all attributes that start with <code>watch-</code> are treated as watches.</p>
<p>If <code>watchExp</code> is set to a regular expression, the regular expression is used to determine if attributes are watches. For instance, if you want watch attribute names to end with <code>-watch</code> you would use the following regular expression:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb40-1" title="1"><span class="at">setConfigs</span>(<span class="op">{</span> <span class="dt">watchExp</span><span class="op">:</span> <span class="ss">/</span><span class="sc">^(\w+)</span><span class="ss">-watch</span><span class="sc">$</span><span class="ss">/</span> <span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>If you're using a regular expression, it must contain parenthesis, as shown above. The parenthesis indicate the location of the name of the variable/element to watch.</p></li>
</ul>
</section>
<section>
<h2 id="license">License</h2>
<p><a href="https://github.com/serg-io/infuse.host/blob/master/LICENSE">MIT</a>.</p>
</section>
</article>
<script>
// Finds elements in the page and returns them as an array.
function queryAll(selector) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}
// Change the `target` of all external links.
queryAll('a[href^="http"]').forEach(function(link) {
link.target = '_blank';
});
/**
* Initially, iframes don't have a src attribute to avoid render blocking. Instead, they
* have data-src attributes. To load iframes, their src attributes must be set using the
* values of their data-src attributes.
*/
var iframes = queryAll('iframe');
/**
* There's a loading message (a <p> element) right before each <iframe>. Add an event
* listerner to each iframe to remove the loading message when the iframe is loaded.
*/
iframes.forEach(function(iframe) {
iframe.addEventListener('load', function(e) {
this.previousElementSibling.remove();
}, false);
});
if (typeof IntersectionObserver === 'undefined') {
// Load the iframes right away if the browser doesn't support IntersectionObserver.
iframes.forEach(function(iframe) {
iframe.src = iframe.getAttribute('data-src');
});
} else {
/**
* If the browser supports IntersectionObserver, wait until each iframe is visible
* before loading them.
*/
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
// If the iframe is visible
if (entry.isIntersecting) {
var iframe = entry.target;
// Set the iframe's src attribute and stop observing it.
iframe.src = iframe.getAttribute('data-src');
observer.unobserve(iframe);
}
});
});
// Observe all iframes.
iframes.forEach(function(iframe) {
observer.observe(iframe);
});
}
</script>
</body>
</html>