@webqit/oohtml 2.1.63 → 2.1.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,38 +5,44 @@
|
|
|
5
5
|
[![bundle][bundle-src]][bundle-href]
|
|
6
6
|
[![License][license-src]][license-href]
|
|
7
7
|
|
|
8
|
-
**[
|
|
8
|
+
**[On the Agenda](#on-the-agenda) • [Modular HTML](#modular-html) • [HTML Imports](#html-imports) • [Data Binding](#data-binding) • [Data Plumbing](#data-plumbing) • [Polyfill](#polyfill) • [Examples](#examples) • [License](#license)**
|
|
9
9
|
|
|
10
|
-
Object-Oriented HTML (OOHTML) is a set of features that extend standard HTML and the DOM to enable authoring modular, reusable and reactive markup - with a "buildless", web-native workflow as design goal! This project presents what "modern
|
|
10
|
+
Object-Oriented HTML (OOHTML) is a set of features that extend standard HTML and the DOM to enable authoring modular, reusable and reactive markup - with a "buildless", web-native workflow as design goal! This project presents what "modern HTML" could look like at its best!
|
|
11
|
+
|
|
12
|
+
Building Single Page Applications? OOHTML is a special love letter!
|
|
13
|
+
|
|
14
|
+
<details><summary>Versions</summary>
|
|
15
|
+
|
|
16
|
+
*This is documentation for `OOHTML@2.x`. (Looking for [`OOHTML@1.x`](https://github.com/webqit/oohtml/tree/v1.10.4)?)*
|
|
17
|
+
|
|
18
|
+
</details>
|
|
11
19
|
|
|
12
20
|
## Motivation
|
|
13
21
|
|
|
14
|
-
Vanilla HTML is
|
|
22
|
+
Vanilla HTML is surprisingly becoming a compelling option for an increasing number of developers! But the current authoring experience still leaves much to be desired in how the language lacks modularity, reusability, and other fundamental capabilities like data binding! Authors still have to rely on tools - or, to say the least, do half of the work in HTML and half in JS - to get even basic things working!
|
|
15
23
|
|
|
16
24
|
This project pursues an object-oriented approach to HTML and implicitly revisits much of what inhibits the idea of a *component* architecture for HTML!
|
|
17
25
|
|
|
18
26
|
└ [See more in the introductory blog post](https://dev.to/oxharris/the-web-native-equations-1m1p-temp-slug-6661657?preview=ba70ad2c17f05b5761bc74516dbde8c9eff8b581a0420d87334fd9ef6bab9d6e6d3ab6aaf3fe02542bb9e7250d0a88a6df91dae40919aabcc9a07320)<sup>draft</sup>
|
|
19
27
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
On the agenda is a set of features that normalises how the modern UI lends itself to be built! And all of it comes as a special love letter to Single Page Applications!
|
|
28
|
+
## On the Agenda
|
|
23
29
|
|
|
24
30
|
+ [Modular HTML](#modular-html)
|
|
25
31
|
+ [HTML Imports](#html-imports)
|
|
26
32
|
+ [Data Binding](#data-binding)
|
|
27
33
|
+ [Data Plumbing](#data-plumbing)
|
|
28
34
|
|
|
29
|
-
> **Note** This is documentation for `OOHTML@2.x`. (Looking for [`OOHTML@1.x`](https://github.com/webqit/oohtml/tree/v1.10.4)?)
|
|
30
|
-
|
|
31
35
|
## Modular HTML
|
|
32
36
|
|
|
33
|
-
Modular HTML is a markup pattern that lets us write markup as self-contained objects - with each *encapsulating* its own structure, styling and logic - as against the regular idea of having everything converge and conflict on one global scope!
|
|
37
|
+
Modular HTML is a markup pattern that lets us write arbitrary markup as self-contained objects - with each *encapsulating* its own structure, styling and logic - as against the regular idea of having everything converge and conflict on one global scope!
|
|
34
38
|
|
|
35
39
|
OOHTML makes this possible in just simple conventions - via two new attributes: `namespace` and `scoped`!
|
|
36
40
|
|
|
37
41
|
### Namespacing
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
Naming things is hard! That's especially so where there's one global namespace and a miriad of potential conflicts - as is the case with HTML!
|
|
44
|
+
|
|
45
|
+
Here, we get the `namespace` attribute for designating an element as own naming context for identifiers instead of the global namespace:
|
|
40
46
|
|
|
41
47
|
```html
|
|
42
48
|
<div id="user" namespace>
|
|
@@ -47,7 +53,7 @@ The `namespace` attribute for designating an element as own naming context for i
|
|
|
47
53
|
</div>
|
|
48
54
|
```
|
|
49
55
|
|
|
50
|
-
*
|
|
56
|
+
**-->** *and this translates really well to an object model*:
|
|
51
57
|
|
|
52
58
|
```html
|
|
53
59
|
user
|
|
@@ -56,7 +62,7 @@ user
|
|
|
56
62
|
└── email
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
*with a corresponding API that exposes said structure to JavaScript applications*:
|
|
65
|
+
**-->** *with a corresponding API that exposes said structure to JavaScript applications*:
|
|
60
66
|
|
|
61
67
|
```js
|
|
62
68
|
// The document.namespace API
|
|
@@ -81,7 +87,9 @@ console.log(window.foo); // div
|
|
|
81
87
|
|
|
82
88
|
</details>
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
<details><summary>All in Realtime</summary>
|
|
91
|
+
|
|
92
|
+
The Namespace API is designed to always reflect the DOM in real-time. This may be observed using the general-purpose object observability API - [Observer API](https://github.com/webqit/observer):
|
|
85
93
|
|
|
86
94
|
```js
|
|
87
95
|
// Observing the addition or removal of elements with an ID
|
|
@@ -108,9 +116,13 @@ function changeCallback(changes) {
|
|
|
108
116
|
}
|
|
109
117
|
```
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
<details>
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
### Scoping
|
|
122
|
+
|
|
123
|
+
We often need a way to keep things like styles and scripts [scoped to a component](https://vuejs.org/guide/scaling-up/sfc.html).
|
|
124
|
+
|
|
125
|
+
Here, we get the `scoped` attribute for *scoping* said element-specific stylesheets and scripts:
|
|
114
126
|
|
|
115
127
|
```html
|
|
116
128
|
<div>
|
|
@@ -126,7 +138,7 @@ The `scoped` attribute for *scoping* element-specific stylesheets and scripts:
|
|
|
126
138
|
</div>
|
|
127
139
|
```
|
|
128
140
|
|
|
129
|
-
*with a corresponding API that exposes said assets to JavaScript applications*:
|
|
141
|
+
**-->** *with a corresponding API that exposes said assets to JavaScript applications*:
|
|
130
142
|
|
|
131
143
|
```js
|
|
132
144
|
let { styleSheets, scripts } = user; // APIs that are analogous to the document.styleSheets, document.scripts properties
|
|
@@ -136,13 +148,15 @@ let { styleSheets, scripts } = user; // APIs that are analogous to the document.
|
|
|
136
148
|
|
|
137
149
|
## HTML Imports
|
|
138
150
|
|
|
139
|
-
HTML Imports is a realtime module system for
|
|
151
|
+
HTML Imports is a realtime module system for HTML written in HTML! Something like it is the [`<defs>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs) and [`<use>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use) system in SVG.
|
|
140
152
|
|
|
141
153
|
OOHTML makes this possible in just simple conventions - via a new `def` attribute and a complementary new `<import>` element!
|
|
142
154
|
|
|
143
155
|
### Module Definition
|
|
144
156
|
|
|
145
|
-
|
|
157
|
+
A module here is any piece of markup that will be reused.
|
|
158
|
+
|
|
159
|
+
Here, we get the `def` attribute for defining those - either as whole *module* or as *fragment*:
|
|
146
160
|
|
|
147
161
|
```html
|
|
148
162
|
<head>
|
|
@@ -156,7 +170,7 @@ The `def` attribute for defining reusable markup - either as whole *module* or a
|
|
|
156
170
|
</head>
|
|
157
171
|
```
|
|
158
172
|
|
|
159
|
-
|
|
173
|
+
**-->** *with module nesting for code organization*:
|
|
160
174
|
|
|
161
175
|
```html
|
|
162
176
|
<head>
|
|
@@ -174,7 +188,9 @@ The `def` attribute for defining reusable markup - either as whole *module* or a
|
|
|
174
188
|
|
|
175
189
|
### Remote Modules
|
|
176
190
|
|
|
177
|
-
|
|
191
|
+
We shouldn't need a different mechanism to work with remote content.
|
|
192
|
+
|
|
193
|
+
Here, we get the `<template src>` element for that:
|
|
178
194
|
|
|
179
195
|
```html
|
|
180
196
|
<template def="foo" src="/foo.html"></template>
|
|
@@ -194,7 +210,7 @@ The `<template src>` element for remote modules:
|
|
|
194
210
|
--
|
|
195
211
|
```
|
|
196
212
|
|
|
197
|
-
*which extends how elements like images already work; terminating with either a `load` or an `error` event*:
|
|
213
|
+
**-->** *which extends how elements like images already work; terminating with either a `load` or an `error` event*:
|
|
198
214
|
|
|
199
215
|
```js
|
|
200
216
|
foo.addEventListener('load', loadCallback);
|
|
@@ -203,7 +219,9 @@ foo.addEventListener('error', errorCallback);
|
|
|
203
219
|
|
|
204
220
|
### Declarative Module Imports
|
|
205
221
|
|
|
206
|
-
The
|
|
222
|
+
The essence of a module is for reuse.
|
|
223
|
+
|
|
224
|
+
Here, we get an `<import>` element that lets us do that declaratively:
|
|
207
225
|
|
|
208
226
|
```html
|
|
209
227
|
<body>
|
|
@@ -260,7 +278,9 @@ Now that extra bit of information gets decoded and original relationships are fo
|
|
|
260
278
|
|
|
261
279
|
### Programmatic Module Imports
|
|
262
280
|
|
|
263
|
-
|
|
281
|
+
JavaScript applications will need more than a declarative import mechanism.
|
|
282
|
+
|
|
283
|
+
Here, we get an *HTMLImports* API for programmatic module import:
|
|
264
284
|
|
|
265
285
|
```js
|
|
266
286
|
const moduleObject1 = document.import('/foo#fragment1');
|
|
@@ -272,7 +292,7 @@ const moduleObject2 = document.import('/foo/nested#fragment2');
|
|
|
272
292
|
console.log(moduleObject2.value); // divElement
|
|
273
293
|
```
|
|
274
294
|
|
|
275
|
-
*with an observable `moduleObject.value` property for working asynchronously; e.g. awaiting and handling remote modules*:
|
|
295
|
+
**-->** *with an observable `moduleObject.value` property for working asynchronously; e.g. awaiting and handling remote modules*:
|
|
276
296
|
|
|
277
297
|
```js
|
|
278
298
|
Observer.observe(moduleObject2, 'value', e => {
|
|
@@ -280,7 +300,7 @@ Observer.observe(moduleObject2, 'value', e => {
|
|
|
280
300
|
});
|
|
281
301
|
```
|
|
282
302
|
|
|
283
|
-
*with an equivalent `callback` option on the `import()` method itself*:
|
|
303
|
+
**-->** *with an equivalent `callback` option on the `import()` method itself*:
|
|
284
304
|
|
|
285
305
|
```js
|
|
286
306
|
document.import('/foo#fragment1', divElement => {
|
|
@@ -288,7 +308,7 @@ document.import('/foo#fragment1', divElement => {
|
|
|
288
308
|
});
|
|
289
309
|
```
|
|
290
310
|
|
|
291
|
-
|
|
311
|
+
**-->** *with an optional `live` parameter for staying subscribed to any mutations made to source module elements*:
|
|
292
312
|
|
|
293
313
|
```js
|
|
294
314
|
const moduleObject2 = document.import('/foo/nested#fragment2', true/*live*/);
|
|
@@ -304,13 +324,13 @@ document.import('/foo#fragment1', true/*live*/, divElement => {
|
|
|
304
324
|
});
|
|
305
325
|
```
|
|
306
326
|
|
|
307
|
-
|
|
327
|
+
*...both of which would get notified on doing the below*:
|
|
308
328
|
|
|
309
329
|
```js
|
|
310
330
|
document.querySelector('template[def="foo"]').content.firstElementChild.remove();
|
|
311
331
|
```
|
|
312
332
|
|
|
313
|
-
|
|
333
|
+
**-->** *with an optional `AbortSignal` parameter for aborting module mutation events*:
|
|
314
334
|
|
|
315
335
|
```js
|
|
316
336
|
const abortController = new AbortController;
|
|
@@ -320,6 +340,14 @@ const abortController = new AbortController;
|
|
|
320
340
|
const moduleObject2 = document.import('/foo/nested#fragment2', { live: true, signal: abortController.signal });
|
|
321
341
|
```
|
|
322
342
|
|
|
343
|
+
*...in the absence of which an `AbortSignal` instance is automatically created internally for the same purpose, such that we're always able to do*:
|
|
344
|
+
|
|
345
|
+
```js
|
|
346
|
+
moduleObject2.abort();
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
*...whereas in the `callback` approach, no automatic `AbortSignals` are created*:
|
|
350
|
+
|
|
323
351
|
```js
|
|
324
352
|
document.import('/foo#fragment1', { live: true, signal: abortController.signal }, divElement => {
|
|
325
353
|
console.log(divElement); // To be received after remote module has been loaded
|
|
@@ -334,7 +362,9 @@ setTimeout(() => abortController.abort(), 1000);
|
|
|
334
362
|
|
|
335
363
|
### Lazy-Loading Modules
|
|
336
364
|
|
|
337
|
-
|
|
365
|
+
We can defer module loading until we really need them.
|
|
366
|
+
|
|
367
|
+
Here, we get the `loading="lazy"` directive for that:
|
|
338
368
|
|
|
339
369
|
```html
|
|
340
370
|
<!-- Loading doesn't happen until the first time this is being accessed -->
|
|
@@ -352,7 +382,9 @@ const moduleObject2 = document.import('/foo#fragment1'); // Triggers module load
|
|
|
352
382
|
|
|
353
383
|
### Scoped Modules
|
|
354
384
|
|
|
355
|
-
|
|
385
|
+
Some modules will only be relevant within a specific context in the page.
|
|
386
|
+
|
|
387
|
+
Here, we get the `scoped` attribute for scoping those to their respective contexts, and thus, implicitly have an *object-scoped* module system:
|
|
356
388
|
|
|
357
389
|
```html
|
|
358
390
|
<section> <!-- object with own modules -->
|
|
@@ -369,7 +401,7 @@ The `scoped` attribute for an *object-scoped* module system:
|
|
|
369
401
|
</section>
|
|
370
402
|
```
|
|
371
403
|
|
|
372
|
-
*with an equivalent `Element.prototype.import()` API for accessing said scoped modules*:
|
|
404
|
+
**-->** *with an equivalent `Element.prototype.import()` API for accessing said scoped modules*:
|
|
373
405
|
|
|
374
406
|
```js
|
|
375
407
|
// Using the HTMLImports API
|
|
@@ -387,7 +419,9 @@ console.log(globalImport1); // { value: div }
|
|
|
387
419
|
|
|
388
420
|
### Module Inheritance
|
|
389
421
|
|
|
390
|
-
|
|
422
|
+
We'll often have repeating markup structures.
|
|
423
|
+
|
|
424
|
+
Here, we get module nesting with inheritance to simplify that:
|
|
391
425
|
|
|
392
426
|
```html
|
|
393
427
|
<template def="foo">
|
|
@@ -433,7 +467,9 @@ Module nesting with inheritance:
|
|
|
433
467
|
|
|
434
468
|
### Imports Contexts
|
|
435
469
|
|
|
436
|
-
|
|
470
|
+
We should be able to define a base path at arbitrary levels in the page against which to resolve import *refs* in subtree.
|
|
471
|
+
|
|
472
|
+
Here, we get "Imports Contexts" for that:
|
|
437
473
|
|
|
438
474
|
```html
|
|
439
475
|
<body importscontext="/foo">
|
|
@@ -458,7 +494,7 @@ const globalImport2 = moduleHost.import('#fragment2'); // module:/foo/nested#fra
|
|
|
458
494
|
console.log(globalImport2); // { value: div }
|
|
459
495
|
```
|
|
460
496
|
|
|
461
|
-
|
|
497
|
+
**-->** *with said "Imports Contexts" optionally having a name*:
|
|
462
498
|
|
|
463
499
|
```html
|
|
464
500
|
<body contextname="context1" importscontext="/foo/nested">
|
|
@@ -482,7 +518,7 @@ const globalImport2 = moduleHost.import('@context1#fragment2'); // module:/foo/n
|
|
|
482
518
|
console.log(globalImport2); // { value: div }
|
|
483
519
|
```
|
|
484
520
|
|
|
485
|
-
|
|
521
|
+
**-->** *with said "Imports Contexts" being able to "nest" nicely*:
|
|
486
522
|
|
|
487
523
|
```html
|
|
488
524
|
<body importscontext="/foo">
|
|
@@ -498,7 +534,9 @@ console.log(globalImport2); // { value: div }
|
|
|
498
534
|
|
|
499
535
|
### Scoped Modules and Imports Contexts
|
|
500
536
|
|
|
501
|
-
|
|
537
|
+
Scoped modules and Import Contexts shouldn't be mutually exclusive.
|
|
538
|
+
|
|
539
|
+
Here, we're able to have *one* element implement *both* at the same time - with scoped modules inheriting Import Contexts:
|
|
502
540
|
|
|
503
541
|
```html
|
|
504
542
|
<body contextname="context1" importscontext="/bar">
|