@webqit/oohtml 2.1.64 → 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 +72 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,44 +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
11
|
|
|
12
12
|
Building Single Page Applications? OOHTML is a special love letter!
|
|
13
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>
|
|
19
|
+
|
|
14
20
|
## Motivation
|
|
15
21
|
|
|
16
|
-
Vanilla HTML is surprisingly becoming a compelling option for an increasing number of
|
|
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!
|
|
17
23
|
|
|
18
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!
|
|
19
25
|
|
|
20
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>
|
|
21
27
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
On the agenda:
|
|
28
|
+
## On the Agenda
|
|
25
29
|
|
|
26
30
|
+ [Modular HTML](#modular-html)
|
|
27
31
|
+ [HTML Imports](#html-imports)
|
|
28
32
|
+ [Data Binding](#data-binding)
|
|
29
33
|
+ [Data Plumbing](#data-plumbing)
|
|
30
34
|
|
|
31
|
-
<details><summary>Version <code>v2.x</code></summary>
|
|
32
|
-
|
|
33
|
-
*This is documentation for `OOHTML@2.x`. (Looking for [`OOHTML@1.x`](https://github.com/webqit/oohtml/tree/v1.10.4)?)*
|
|
34
|
-
|
|
35
|
-
</details>
|
|
36
|
-
|
|
37
35
|
## Modular HTML
|
|
38
36
|
|
|
39
|
-
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!
|
|
40
38
|
|
|
41
39
|
OOHTML makes this possible in just simple conventions - via two new attributes: `namespace` and `scoped`!
|
|
42
40
|
|
|
43
41
|
### Namespacing
|
|
44
42
|
|
|
45
|
-
|
|
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:
|
|
46
46
|
|
|
47
47
|
```html
|
|
48
48
|
<div id="user" namespace>
|
|
@@ -53,7 +53,7 @@ The `namespace` attribute for designating an element as own naming context for i
|
|
|
53
53
|
</div>
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
*
|
|
56
|
+
**-->** *and this translates really well to an object model*:
|
|
57
57
|
|
|
58
58
|
```html
|
|
59
59
|
user
|
|
@@ -62,7 +62,7 @@ user
|
|
|
62
62
|
└── email
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
*with a corresponding API that exposes said structure to JavaScript applications*:
|
|
65
|
+
**-->** *with a corresponding API that exposes said structure to JavaScript applications*:
|
|
66
66
|
|
|
67
67
|
```js
|
|
68
68
|
// The document.namespace API
|
|
@@ -87,7 +87,9 @@ console.log(window.foo); // div
|
|
|
87
87
|
|
|
88
88
|
</details>
|
|
89
89
|
|
|
90
|
-
|
|
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):
|
|
91
93
|
|
|
92
94
|
```js
|
|
93
95
|
// Observing the addition or removal of elements with an ID
|
|
@@ -114,9 +116,13 @@ function changeCallback(changes) {
|
|
|
114
116
|
}
|
|
115
117
|
```
|
|
116
118
|
|
|
117
|
-
|
|
119
|
+
<details>
|
|
120
|
+
|
|
121
|
+
### Scoping
|
|
118
122
|
|
|
119
|
-
|
|
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:
|
|
120
126
|
|
|
121
127
|
```html
|
|
122
128
|
<div>
|
|
@@ -132,7 +138,7 @@ The `scoped` attribute for *scoping* element-specific stylesheets and scripts:
|
|
|
132
138
|
</div>
|
|
133
139
|
```
|
|
134
140
|
|
|
135
|
-
*with a corresponding API that exposes said assets to JavaScript applications*:
|
|
141
|
+
**-->** *with a corresponding API that exposes said assets to JavaScript applications*:
|
|
136
142
|
|
|
137
143
|
```js
|
|
138
144
|
let { styleSheets, scripts } = user; // APIs that are analogous to the document.styleSheets, document.scripts properties
|
|
@@ -142,13 +148,15 @@ let { styleSheets, scripts } = user; // APIs that are analogous to the document.
|
|
|
142
148
|
|
|
143
149
|
## HTML Imports
|
|
144
150
|
|
|
145
|
-
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.
|
|
146
152
|
|
|
147
153
|
OOHTML makes this possible in just simple conventions - via a new `def` attribute and a complementary new `<import>` element!
|
|
148
154
|
|
|
149
155
|
### Module Definition
|
|
150
156
|
|
|
151
|
-
|
|
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*:
|
|
152
160
|
|
|
153
161
|
```html
|
|
154
162
|
<head>
|
|
@@ -162,7 +170,7 @@ The `def` attribute for defining reusable markup - either as whole *module* or a
|
|
|
162
170
|
</head>
|
|
163
171
|
```
|
|
164
172
|
|
|
165
|
-
|
|
173
|
+
**-->** *with module nesting for code organization*:
|
|
166
174
|
|
|
167
175
|
```html
|
|
168
176
|
<head>
|
|
@@ -180,7 +188,9 @@ The `def` attribute for defining reusable markup - either as whole *module* or a
|
|
|
180
188
|
|
|
181
189
|
### Remote Modules
|
|
182
190
|
|
|
183
|
-
|
|
191
|
+
We shouldn't need a different mechanism to work with remote content.
|
|
192
|
+
|
|
193
|
+
Here, we get the `<template src>` element for that:
|
|
184
194
|
|
|
185
195
|
```html
|
|
186
196
|
<template def="foo" src="/foo.html"></template>
|
|
@@ -200,7 +210,7 @@ The `<template src>` element for remote modules:
|
|
|
200
210
|
--
|
|
201
211
|
```
|
|
202
212
|
|
|
203
|
-
*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*:
|
|
204
214
|
|
|
205
215
|
```js
|
|
206
216
|
foo.addEventListener('load', loadCallback);
|
|
@@ -209,7 +219,9 @@ foo.addEventListener('error', errorCallback);
|
|
|
209
219
|
|
|
210
220
|
### Declarative Module Imports
|
|
211
221
|
|
|
212
|
-
The
|
|
222
|
+
The essence of a module is for reuse.
|
|
223
|
+
|
|
224
|
+
Here, we get an `<import>` element that lets us do that declaratively:
|
|
213
225
|
|
|
214
226
|
```html
|
|
215
227
|
<body>
|
|
@@ -266,7 +278,9 @@ Now that extra bit of information gets decoded and original relationships are fo
|
|
|
266
278
|
|
|
267
279
|
### Programmatic Module Imports
|
|
268
280
|
|
|
269
|
-
|
|
281
|
+
JavaScript applications will need more than a declarative import mechanism.
|
|
282
|
+
|
|
283
|
+
Here, we get an *HTMLImports* API for programmatic module import:
|
|
270
284
|
|
|
271
285
|
```js
|
|
272
286
|
const moduleObject1 = document.import('/foo#fragment1');
|
|
@@ -278,7 +292,7 @@ const moduleObject2 = document.import('/foo/nested#fragment2');
|
|
|
278
292
|
console.log(moduleObject2.value); // divElement
|
|
279
293
|
```
|
|
280
294
|
|
|
281
|
-
*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*:
|
|
282
296
|
|
|
283
297
|
```js
|
|
284
298
|
Observer.observe(moduleObject2, 'value', e => {
|
|
@@ -286,7 +300,7 @@ Observer.observe(moduleObject2, 'value', e => {
|
|
|
286
300
|
});
|
|
287
301
|
```
|
|
288
302
|
|
|
289
|
-
*with an equivalent `callback` option on the `import()` method itself*:
|
|
303
|
+
**-->** *with an equivalent `callback` option on the `import()` method itself*:
|
|
290
304
|
|
|
291
305
|
```js
|
|
292
306
|
document.import('/foo#fragment1', divElement => {
|
|
@@ -294,7 +308,7 @@ document.import('/foo#fragment1', divElement => {
|
|
|
294
308
|
});
|
|
295
309
|
```
|
|
296
310
|
|
|
297
|
-
|
|
311
|
+
**-->** *with an optional `live` parameter for staying subscribed to any mutations made to source module elements*:
|
|
298
312
|
|
|
299
313
|
```js
|
|
300
314
|
const moduleObject2 = document.import('/foo/nested#fragment2', true/*live*/);
|
|
@@ -310,13 +324,13 @@ document.import('/foo#fragment1', true/*live*/, divElement => {
|
|
|
310
324
|
});
|
|
311
325
|
```
|
|
312
326
|
|
|
313
|
-
|
|
327
|
+
*...both of which would get notified on doing the below*:
|
|
314
328
|
|
|
315
329
|
```js
|
|
316
330
|
document.querySelector('template[def="foo"]').content.firstElementChild.remove();
|
|
317
331
|
```
|
|
318
332
|
|
|
319
|
-
|
|
333
|
+
**-->** *with an optional `AbortSignal` parameter for aborting module mutation events*:
|
|
320
334
|
|
|
321
335
|
```js
|
|
322
336
|
const abortController = new AbortController;
|
|
@@ -326,6 +340,14 @@ const abortController = new AbortController;
|
|
|
326
340
|
const moduleObject2 = document.import('/foo/nested#fragment2', { live: true, signal: abortController.signal });
|
|
327
341
|
```
|
|
328
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
|
+
|
|
329
351
|
```js
|
|
330
352
|
document.import('/foo#fragment1', { live: true, signal: abortController.signal }, divElement => {
|
|
331
353
|
console.log(divElement); // To be received after remote module has been loaded
|
|
@@ -340,7 +362,9 @@ setTimeout(() => abortController.abort(), 1000);
|
|
|
340
362
|
|
|
341
363
|
### Lazy-Loading Modules
|
|
342
364
|
|
|
343
|
-
|
|
365
|
+
We can defer module loading until we really need them.
|
|
366
|
+
|
|
367
|
+
Here, we get the `loading="lazy"` directive for that:
|
|
344
368
|
|
|
345
369
|
```html
|
|
346
370
|
<!-- Loading doesn't happen until the first time this is being accessed -->
|
|
@@ -358,7 +382,9 @@ const moduleObject2 = document.import('/foo#fragment1'); // Triggers module load
|
|
|
358
382
|
|
|
359
383
|
### Scoped Modules
|
|
360
384
|
|
|
361
|
-
|
|
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:
|
|
362
388
|
|
|
363
389
|
```html
|
|
364
390
|
<section> <!-- object with own modules -->
|
|
@@ -375,7 +401,7 @@ The `scoped` attribute for an *object-scoped* module system:
|
|
|
375
401
|
</section>
|
|
376
402
|
```
|
|
377
403
|
|
|
378
|
-
*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*:
|
|
379
405
|
|
|
380
406
|
```js
|
|
381
407
|
// Using the HTMLImports API
|
|
@@ -393,7 +419,9 @@ console.log(globalImport1); // { value: div }
|
|
|
393
419
|
|
|
394
420
|
### Module Inheritance
|
|
395
421
|
|
|
396
|
-
|
|
422
|
+
We'll often have repeating markup structures.
|
|
423
|
+
|
|
424
|
+
Here, we get module nesting with inheritance to simplify that:
|
|
397
425
|
|
|
398
426
|
```html
|
|
399
427
|
<template def="foo">
|
|
@@ -439,7 +467,9 @@ Module nesting with inheritance:
|
|
|
439
467
|
|
|
440
468
|
### Imports Contexts
|
|
441
469
|
|
|
442
|
-
|
|
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:
|
|
443
473
|
|
|
444
474
|
```html
|
|
445
475
|
<body importscontext="/foo">
|
|
@@ -464,7 +494,7 @@ const globalImport2 = moduleHost.import('#fragment2'); // module:/foo/nested#fra
|
|
|
464
494
|
console.log(globalImport2); // { value: div }
|
|
465
495
|
```
|
|
466
496
|
|
|
467
|
-
|
|
497
|
+
**-->** *with said "Imports Contexts" optionally having a name*:
|
|
468
498
|
|
|
469
499
|
```html
|
|
470
500
|
<body contextname="context1" importscontext="/foo/nested">
|
|
@@ -488,7 +518,7 @@ const globalImport2 = moduleHost.import('@context1#fragment2'); // module:/foo/n
|
|
|
488
518
|
console.log(globalImport2); // { value: div }
|
|
489
519
|
```
|
|
490
520
|
|
|
491
|
-
|
|
521
|
+
**-->** *with said "Imports Contexts" being able to "nest" nicely*:
|
|
492
522
|
|
|
493
523
|
```html
|
|
494
524
|
<body importscontext="/foo">
|
|
@@ -504,7 +534,9 @@ console.log(globalImport2); // { value: div }
|
|
|
504
534
|
|
|
505
535
|
### Scoped Modules and Imports Contexts
|
|
506
536
|
|
|
507
|
-
|
|
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:
|
|
508
540
|
|
|
509
541
|
```html
|
|
510
542
|
<body contextname="context1" importscontext="/bar">
|