You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: README.md
+29-7
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,8 @@
2
2
3
3
UIElement - the "look ma, no JS framework!" library bringing signals-based reactivity to vanilla Web Components
4
4
5
+
Version 0.6.2
6
+
5
7
## What is UIElement?
6
8
7
9
`UIElement` is a base class for your reactive Web Components. It extends the native `HTMLElement` class and adds a public property and a few methods that allow you to implement inter- and intra-component reactivity with ease. You extend the base class `UIElement` and call the static `define()` method on it to register a tag name in the `CustomElementsRegistry`.
@@ -10,19 +12,19 @@ UIElement - the "look ma, no JS framework!" library bringing signals-based react
10
12
11
13
`UIElement` implements a `Map`-like interface on top of `HTMLElement` to access and modify reactive states. The method names `this.has()`, `this.get()`, `this.set()` and `this.delete()` feel familar to JavaScript developers and mirror what you already know.
12
14
13
-
In the `connectedCallback()` you setup references to inner elements, add event listeners and pass reactive states to sub-components (`this.pass()`). Additionally, for every independent reactive state you define what happens when it changes in the callback of `this.effect()`. `UIElement` will automatically trigger these effects and bundle the surgical DOM updates when the browser refreshes the view on the next animation frame.
15
+
In the `connectedCallback()` you setup references to inner elements, add event listeners and pass reactive states to sub-components (`this.pass()`). Additionally, for every independent reactive state you define what happens when it changes in the callback of `this.effect()`. `UIElement` will automatically trigger these effects and bundle the fine-grained DOM updates when the browser refreshes the view on the next animation frame.
14
16
15
-
`UIElement` is fast. In fact, faster than any JavaScript framework. Only direct surgical DOM updates in vanilla JavaScript can beat its performance. But then, you have no loose coupling of components and need to parse attributes and track changes yourself. This tends to get tedious and messy rather quickly. `UIElement` provides a structured way to keep your components simple, consistent and self-contained.
17
+
`UIElement` is fast. In fact, faster than any JavaScript framework. Only direct fine-grained DOM updates in vanilla JavaScript can beat its performance. But then, you have no loose coupling of components and need to parse attributes and track changes yourself. This tends to get tedious and messy rather quickly. `UIElement` provides a structured way to keep your components simple, consistent and self-contained.
16
18
17
-
`UIElement` is tiny. 681 bytes gzipped over the wire. And it has zero dependiences. If you want to understand how it works, you have to study the source code of [one single file](./index.js).
19
+
`UIElement` is tiny. 685 bytes gzipped over the wire. And it has zero dependiences. If you want to understand how it works, you have to study the source code of [one single file](./index.js).
18
20
19
21
That's all.
20
22
21
23
## What is UIElement intentionally not?
22
24
23
25
`UIElement` does not do many of the things JavaScript frameworks do.
24
26
25
-
Most importantly, it does not render components. We suggest, you render components (eighter Light DOM children or Declarative Shadow DOM) on the server side. There are existing solutions like [WebC](https://github.com/11ty/webc) or [Enhance](https://github.com/enhance-dev/enhance) that allow you to declare and render Web Components on the server side with (almost) pure HTML, CSS and JavaScript. `UIElement` is proven to work with either WebC or Enhance. But you could use any tech stack able to render HTML. There is no magic involved besides the building blocks of any website: HTML, CSS and JavaScript. `UIElement` does not make any assumptions about the structure of the inner HTML. In fact, it is up to you to reference inner elements and do surgical DOM updates in effects. This also means, there is no new language or format to learn. HTML, CSS and modern JavaScript (ES6) is all you need to know to develop your own web components with `UIElement`.
27
+
Most importantly, it does not render components. We suggest, you render components (eighter Light DOM children or Declarative Shadow DOM) on the server side. There are existing solutions like [WebC](https://github.com/11ty/webc) or [Enhance](https://github.com/enhance-dev/enhance) that allow you to declare and render Web Components on the server side with (almost) pure HTML, CSS and JavaScript. `UIElement` is proven to work with either WebC or Enhance. But you could use any tech stack able to render HTML. There is no magic involved besides the building blocks of any website: HTML, CSS and JavaScript. `UIElement` does not make any assumptions about the structure of the inner HTML. In fact, it is up to you to reference inner elements and do fine-grained DOM updates in effects. This also means, there is no new language or format to learn. HTML, CSS and modern JavaScript (ES6) is all you need to know to develop your own web components with `UIElement`.
26
28
27
29
`UIElement` does no routing. It is strictly for single-page applications or reactive islands. But of course, you can reuse the same components on many different pages, effectively creating tailored single-page applications for every page you want to enhance with rich interactivity. We believe, this is the most efficient way to build rich multi-page applications, as only the scripts for the elements used on the current page are loaded, not a huge bundle for the whole app.
28
30
@@ -160,11 +162,31 @@ Make sure the import of `UIElement` on the first line points to your installed p
160
162
importUIElementfrom'@efflore/ui-element';
161
163
```
162
164
165
+
If you use Debug Element as your base class for custom elements, you may call `super.connectedCallback();` (and the other lifecycle callbacks) to log when your element connects to the DOM.
166
+
167
+
To log when some DOM features of child elements are updated in effects, you need to enqueue all fine-grained DOM updated like this:
Otherwise Debug Element only knows which effect runs in which component, but not the exact elements targeted by your effect.
175
+
176
+
Enqueueing fine-grained DOM updates is always possible. It's a bit more verbose, but it ensures all updates of your effect happen at the same time. `autoEffects()` from DOM Utils (see next section) does this by default. All DOM utility functions receive the target element as first parameter, making it possible to use this shorter notation:
A few utility functions for surgical DOM updates in `effect()`s that streamline the interface and save tedious existance and change checks:
189
+
A few utility functions for fine-grained DOM updates in `effect()`s that streamline the interface and save tedious existance and change checks:
168
190
169
191
-`setText()` preserves comment nodes in contrast to `element.textContent` assignments
170
192
-`setProp()` sets or deletes a property on an element
@@ -184,7 +206,7 @@ With all key/value pair attributes, you can provide several separated by `;`. Ea
184
206
185
207
Auto-Effects will be be applied to the Shadow DOM, if your component uses it; otherwise to the Light DOM sub-tree. The `ui-*` attributes will be removed from the DOM once your component is connected and the effects are set up. If you load a partial from the server containing these attributes, you will have to call `autoEffects()` again to have the effects auto-applied to the newly loaded partial as well.
186
208
187
-
By using these declarative attributes you can considerably reduce the amount of simple effects in your component's JavaScript. Almost all surgical DOM updates can be done this way. You gain Locality of Behavior (LoB) in your markup and can decide per case where effects shall be applied. On the other hand, you lose Separation of Concerns (SoC) - if you care about it. It's the same trade-off as with JSX, but in pure HTML.
209
+
By using these declarative attributes you can considerably reduce the amount of simple effects in your component's JavaScript. Almost all fine-grained DOM updates can be done this way. You gain Locality of Behavior (LoB) in your markup and can decide per case where effects shall be applied. On the other hand, you lose Separation of Concerns (SoC) - if you care about it. It's the same trade-off as with JSX, but in pure HTML.
188
210
189
211
As not all users like the sort of magic of Auto-Effects, it's an optional opt-in and not part of the core `UIElement` library. Copy the source code and adapt it to your needs, if you like it.
190
212
@@ -237,7 +259,7 @@ Where has the JavaScript gone? – It almost disappeared. To explain the magic:
237
259
6.`UIElement`**auto-runs** the effect you did not even write again with a new `'value'` value
238
260
7.`autoEffects()` knows which element's `textContent` to **auto-update**
239
261
240
-
By always following this pattern of data-flow, that is close to an optimal implementation in vanilla JavaScript, we can drastrically reduce need JavaScrpt both on the library side (`UIElement` + `dom-utils` ca. 1.3 kB gzipped) and on userland side.
262
+
By always following this pattern of data-flow, that is close to an optimal implementation in vanilla JavaScript, we can drastrically reduce need JavaScrpt both on the library side (`UIElement` + `dom-utils` ca. 1.4 kB gzipped) and on userland side.
* @param {Element} element - element to be updated
90
99
* @param {PropertyKey} key - property to be updated
91
100
* @param {any} value - new property value; `''` or `true` for boolean attribute; `null` for opt-out of update; `undefined` or `false` will delete existing property
* @param {Element} element - element to be toggled
110
121
* @param {string} token - class token to be toggled
111
122
* @param {boolean|null|undefined} force - force toggle condition `true` or `false`; `null` for opt-out of update; `undefined` will toggle existing class
0 commit comments