The library tries to provide pure JavaScript tool(s) to create reactive interfaces.
We start with creating and modifying a static elements and end up with UI templates. From document.createElement
to el
. Then we go through the native events system and the way to include it declaratively in UI templates. From element.addEventListener
to on
.
Next step is providing interactivity not only for our UI templates. We introduce signals (S
) and how them incorporate to UI templates.
Now we will clarify how the signals are incorporated into our templates with regard to application performance. This is not the only reason the library uses scope
s. We will look at how they work in components represented in JavaScript by functions.
import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+`deka-dom-el` — Introduction `deka-dom-el` — Introduction
Introducing a library.
The library tries to provide pure JavaScript tool(s) to create reactive interfaces.
We start with creating and modifying a static elements and end up with UI templates. From document.createElement
to el
. Then we go through the native events system and the way to include it declaratively in UI templates. From element.addEventListener
to on
.
Next step is providing interactivity not only for our UI templates. We introduce signals (S
) and how them incorporate to UI templates.
Now we will clarify how the signals are incorporated into our templates with regard to application performance. This is not the only reason the library uses scope
s. We will look at how they work in components represented in JavaScript by functions.
import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
import { S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const clicks= S(0);
document.body.append(
diff --git a/docs/p02-elements.html b/docs/p02-elements.html
index 44b12fc..478f9e0 100644
--- a/docs/p02-elements.html
+++ b/docs/p02-elements.html
@@ -1,4 +1,4 @@
-`deka-dom-el` — Elements `deka-dom-el` — Elements
Basic concepts of elements modifications and creations.
Native JavaScript DOM elements creations
Let’s go through all patterns we would like to use and what needs to be improved for better experience.
§ Creating element(s) (with custom attributes)
You can create a native DOM element by using the document.createElement()
. It is also possible to provide a some attribute(s) declaratively using Object.assign()
. More precisely, this way you can sets some IDL also known as a JavaScript property.
document.body.append(
+`deka-dom-el` — Elements `deka-dom-el` — Elements
Basic concepts of elements modifications and creations.
Native JavaScript DOM elements creations
Let’s go through all patterns we would like to use and what needs to be improved for better experience.
# Creating element(s) (with custom attributes)
You can create a native DOM element by using the document.createElement()
. It is also possible to provide a some attribute(s) declaratively using Object.assign()
. More precisely, this way you can sets some IDL also known as a JavaScript property.
document.body.append(
document.createElement("div")
);
console.log(
@@ -56,7 +56,7 @@ console.log("paragraph.something=", paragraph.something);
document.body.append(
paragraph
);
-
§ Native JavaScript templating
By default, the native JS has no good way to define HTML template using DOM API:
document.body.append(
+
# Native JavaScript templating
By default, the native JS has no good way to define HTML template using DOM API:
document.body.append(
document.createElement("div"),
document.createElement("span"),
document.createElement("main")
@@ -102,7 +102,7 @@ document.body.append(
)
)
);
-
§ Basic (state-less) components
You can use functions for encapsulation (repeating) logic. The el
accepts function as first argument. In that case, the function should return dom elements and the second argument for el
is argument for given element.
import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+
# Basic (state-less) components
You can use functions for encapsulation (repeating) logic. The el
accepts function as first argument. In that case, the function should return dom elements and the second argument for el
is argument for given element.
import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
document.head.append(
el("style").append(
".class1{ font-weight: bold; }",
@@ -119,7 +119,7 @@ function component({ className, textContent }){
el("p", textContent)
);
}
-
As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el
function.
It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).
§ Creating non-HTML elements
Similarly to the native DOM API (document.createElementNS
) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS
function:
import { elNS, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+
As you can see, in case of state-less/basic components there is no difference between calling component function directly or using el
function.
It is nice to use similar naming convention as native DOM API. This allows us to use the destructuring assignment syntax and keep track of the native API (things are best remembered through regular use).
# Creating non-HTML elements
Similarly to the native DOM API (document.createElementNS
) for non-HTML elements we need to tell JavaScript which kind of the element to create. We can use the elNS
function:
import { elNS, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const elSVG= elNS("http://www.w3.org/2000/svg");
const elMath= elNS("http://www.w3.org/1998/Math/MathML");
document.body.append(
@@ -130,4 +130,4 @@ document.body.append(
console.log(
document.body.innerHTML.includes("<svg></svg><math></math>")
)
-
\ No newline at end of file
+
Mnemonic
assign(<element>, ...<idl-objects>): <element>
— assign properties to the elementel(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>
— simple element containing only textel(<tag-name>, <idl-object>)[.append(...)]: <element-from-tag-name>
— element with more propertiesel(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>
— using component represented by functionel(<...>, <...>, ...<addons>)
— see following pageelNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>
— typically SVG elements
\ No newline at end of file
diff --git a/docs/p03-events.html b/docs/p03-events.html
index df763ba..d53c185 100644
--- a/docs/p03-events.html
+++ b/docs/p03-events.html
@@ -1,4 +1,4 @@
-`deka-dom-el` — Events and Addons `deka-dom-el` — Events and Addons
Using not only events in UI declaratively.
Listenning to the native DOM events and other Addons
We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called Addon to incorporate not only this in UI templates declaratively.
§ Events and listenners
In JavaScript you can listen to the native DOM events of the given element by using element.addEventListener(type, listener, options)
. The library provides an alternative (on
) accepting the differen order of the arguments:
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+`deka-dom-el` — Events and Addons `deka-dom-el` — Events and Addons
Using not only events in UI declaratively.
Listenning to the native DOM events and other Addons
We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called Addon to incorporate not only this in UI templates declaratively.
# Events and listenners
In JavaScript you can listen to the native DOM events of the given element by using element.addEventListener(type, listener, options)
. The library provides an alternative (on
) accepting the differen order of the arguments:
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const log= mark=> console.log.bind(console, mark);
const button= el("button", "Test click");
@@ -21,7 +21,7 @@ on("click", log("`on`"), { signal })(button);
document.body.append(
button, " ", el("button", { textContent: "Off", onclick: ()=> abort_controller.abort() })
);
-
So, there are (typically) three ways to handle events. You can use:
el("button", { textContent: "click me", "=onclick": "console.log(event)" })
el("button", { textContent: "click me", onclick: console.log })
el("button", { textContent: "click me" }, on("click", console.log))
In the first example we force to use HTML attribute (it corresponds to <button onclick="console.log(event)">click me</button>
). Side note: this can be useful in case of SSR. To study difference, you can read a nice summary here: GIST @WebReflection/web_events.md.
§ Addons
From practical point of view, Addons are just functions that accept any html element as their first parameter. You can see that the on(…)
fullfills this requirement.
You can use Addons as ≥3rd argument of el
function. This way is possible to extends you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+
So, there are (typically) three ways to handle events. You can use:
el("button", { textContent: "click me", "=onclick": "console.log(event)" })
el("button", { textContent: "click me", onclick: console.log })
el("button", { textContent: "click me" }, on("click", console.log))
In the first example we force to use HTML attribute (it corresponds to <button onclick="console.log(event)">click me</button>
). Side note: this can be useful in case of SSR. To study difference, you can read a nice summary here: GIST @WebReflection/web_events.md.
# Addons
From practical point of view, Addons are just functions that accept any html element as their first parameter. You can see that the on(…)
fullfills this requirement.
You can use Addons as ≥3rd argument of el
function. This way is possible to extends you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const abort_controller= new AbortController();
const { signal }= abort_controller;
/** @type {ddeElementAddon<HTMLButtonElement>} */
@@ -45,7 +45,7 @@ function update(event){
"\n"
);
}
-
As the example shows, you can also provide types in JSDoc+TypeScript by using global type ddeElementAddon
. Also notice, you can use Addons to get element reference.
§ Life-cycle events
Addons are called immediately when the element is created, even it is not connected to live DOM yet. Therefore, you can understand the Addon to be “oncreate” event.
The library provide three additional live-cycle events corresponding to how they are named in a case of custom elements: on.connected
, on.disconnected
and on.attributeChanged
.
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+
As the example shows, you can also provide types in JSDoc+TypeScript by using global type ddeElementAddon
. Also notice, you can use Addons to get element reference.
# Life-cycle events
Addons are called immediately when the element is created, even it is not connected to live DOM yet. Therefore, you can understand the Addon to be “oncreate” event.
The library provide three additional live-cycle events corresponding to how they are named in a case of custom elements: on.connected
, on.disconnected
and on.attributeChanged
.
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const paragraph= el("p", "See live-cycle events in console.",
el=> log({ type: "dde:created", detail: el }),
on.connected(log),
@@ -63,7 +63,7 @@ document.body.append(
function log({ type, detail }){
console.log({ _this: this, type, detail });
}
-
For Custom elements, we will later introduce a way to replace *Callback
syntax with dde:*
events. The on.*
functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).
But, in case of regular elemnets the MutationObserver
| MDN is internaly used to track these events. Therefore, there are some drawbacks:
- To proper listener registration, you need to use
on.*
not `on("dde:*", …)`! - Use sparingly! Internally, library must loop of all registered events and fires event properly. It is good practice to use the fact that if an element is removed, its children are also removed! In this spirit, we will introduce later the host syntax to register clean up procedures when the component is removed from the app.
§ Final notes
The library also provides a method to dispatch the events.
import { el, on, dispatchEvent } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+
For Custom elements, we will later introduce a way to replace *Callback
syntax with dde:*
events. The on.*
functions then listen to the appropriate Custom Elements events (see Custom element lifecycle callbacks | MDN).
But, in case of regular elemnets the MutationObserver
| MDN is internaly used to track these events. Therefore, there are some drawbacks:
- To proper listener registration, you need to use
on.*
not `on("dde:*", …)`! - Use sparingly! Internally, library must loop of all registered events and fires event properly. It is good practice to use the fact that if an element is removed, its children are also removed! In this spirit, we will introduce later the host syntax to register clean up procedures when the component is removed from the app.
# Final notes
The library also provides a method to dispatch the events.
import { el, on, dispatchEvent } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
document.body.append(
el("p", "Listenning to `test` event.", on("test", console.log)).append(
el("br"),
@@ -87,4 +87,4 @@ function dde(){
function ddeOptions(){
dispatchEvent("test", { bubbles: true })(this, "hi");
}
-
\ No newline at end of file
+
Mnemonic
on(<event>, <listener>[, <options>])(<element>)
— just <element>.addEventListener(<event>, <listener>[, <options>])
on.<live-cycle>(<event>, <listener>[, <options>])(<element>)
— corresponds to custom elemnets callbacks <live-cycle>Callback(...){...}
. To connect to custom element see following page, else it is simulated by MutationObserver.dispatchEvent(<event>[, <options>])(element)
— just <element>.dispatchEvent(new Event(<event>[, <options>]))
dispatchEvent(<event>[, <options>])(element, detail)
— just <element>.dispatchEvent(new CustomEvent(<event>, { detail, ...<options> }))
\ No newline at end of file
diff --git a/docs/p04-signals.html b/docs/p04-signals.html
new file mode 100644
index 0000000..7df5631
--- /dev/null
+++ b/docs/p04-signals.html
@@ -0,0 +1,25 @@
+`deka-dom-el` — Signals and reactivity `deka-dom-el` — Signals and reactivity
Handling reactivity in UI via signals.
Using signals to manage reactivity
How a program responds to variable data or user interactions is one of the fundamental problems of programming. If we desire to solve the issue in a declarative manner, signals may be a viable approach.
import { S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+// α — `signal` represents a reactive value
+const signal= S(0);
+// β — just reacts on signal changes
+S.on(signal, console.log);
+// γ — just updates the value
+signal(signal()+1);
+setInterval(()=> signal(signal()+1), 5000);
+
# Introducing signals
Using signals, we split program logic into the three parts. Firstly (α), we create a variable (constant) representing reactive value. Somewhere later, we can register (β) a logic reacting to the signal value changes. Similarly, in a remaining part (γ), we can update the signal value.
All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.
Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal())
. To update the signal value, pass any argument signal('a new value')
. For listenning the signal value changes, use S.on(signal, console.log)
.
Similarly to the on
function to register DOM events listener. You can use AbortController
/AbortSignal
to off/stop listenning. For representing “live” piece of code computation pattern:
import { S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+const signal= S(0);
+// computation pattern
+const double= S(()=> 2*signal());
+
+const ac= new AbortController();
+S.on(signal, v=> console.log("signal", v), { signal: ac.signal });
+S.on(double, v=> console.log("double", v), { signal: ac.signal });
+
+signal(signal()+1);
+const interval= 5000;
+const id= setInterval(()=> signal(signal()+1), interval);
+ac.signal.addEventListener("abort",
+ ()=> setTimeout(()=> clearInterval(id), 2*interval));
+
+setTimeout(()=> ac.abort(), 3*interval)
+
Mnemonic
S(<value>)
— signal: reactive valueS(()=> <computation>)
— signal: reactive value dependent on calculation using other signalsS.on(<signal>, <listener>[, <options>])
— listen to the signal value changesS.clear(...<signals>)
— off and clear signalsS(<value>, <actions>)
— signal: pattern to create complex reactive objects/arraysS.action(<signal>, <action-name>, ...<action-arguments>)
— invoke an action for given signal
\ No newline at end of file
diff --git a/docs_src/components/examples/signals/computations-abort.js b/docs_src/components/examples/signals/computations-abort.js
new file mode 100644
index 0000000..c7702c0
--- /dev/null
+++ b/docs_src/components/examples/signals/computations-abort.js
@@ -0,0 +1,16 @@
+import { S } from "deka-dom-el/signals";
+const signal= S(0);
+// computation pattern
+const double= S(()=> 2*signal());
+
+const ac= new AbortController();
+S.on(signal, v=> console.log("signal", v), { signal: ac.signal });
+S.on(double, v=> console.log("double", v), { signal: ac.signal });
+
+signal(signal()+1);
+const interval= 5000;
+const id= setInterval(()=> signal(signal()+1), interval);
+ac.signal.addEventListener("abort",
+ ()=> setTimeout(()=> clearInterval(id), 2*interval));
+
+setTimeout(()=> ac.abort(), 3*interval)
diff --git a/docs_src/components/examples/signals/intro.js b/docs_src/components/examples/signals/intro.js
new file mode 100644
index 0000000..f9b6d52
--- /dev/null
+++ b/docs_src/components/examples/signals/intro.js
@@ -0,0 +1,8 @@
+import { S } from "deka-dom-el/signals";
+// α — `signal` represents a reactive value
+const signal= S(0);
+// β — just reacts on signal changes
+S.on(signal, console.log);
+// γ — just updates the value
+signal(signal()+1);
+setInterval(()=> signal(signal()+1), 5000);
diff --git a/docs_src/components/pageUtils.html.js b/docs_src/components/pageUtils.html.js
index 44ebf7c..ffd721b 100644
--- a/docs_src/components/pageUtils.html.js
+++ b/docs_src/components/pageUtils.html.js
@@ -24,7 +24,7 @@ import { el } from "../../index.js";
export function h3({ textContent, id }){
if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
return el("h3", { id }).append(
- el("a", { textContent: "§", href: "#"+id, tabIndex: -1 }),
+ el("a", { textContent: "#", href: "#"+id, tabIndex: -1 }),
" ", textContent
);
}
diff --git a/docs_src/p02-elements.html.js b/docs_src/p02-elements.html.js
index 5d46f8c..4665607 100644
--- a/docs_src/p02-elements.html.js
+++ b/docs_src/p02-elements.html.js
@@ -116,6 +116,30 @@ export function page({ pkg, info }){
" We can use the ", el("code", "elNS"), " function:"
),
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
+
+ el("div", { className: "notice" }).append(
+ el("p", "Mnemonic"),
+ el("ul").append(
+ el("li").append(
+ el("code", "assign(, ...): "), " — assign properties to the element",
+ ),
+ el("li").append(
+ el("code", "el(, )[.append(...)]: "), " — simple element containing only text",
+ ),
+ el("li").append(
+ el("code", "el(, )[.append(...)]: "), " — element with more properties",
+ ),
+ el("li").append(
+ el("code", "el(, )[.append(...)]: "), " — using component represented by function",
+ ),
+ el("li").append(
+ el("code", "el(<...>, <...>, ...)"), " — see following page"
+ ),
+ el("li").append(
+ el("code", "elNS()()[.append(...)]: "), " — typically SVG elements",
+ )
+ )
+ ),
el(prevNext, info)
)
diff --git a/docs_src/p03-events.html.js b/docs_src/p03-events.html.js
index a3845ce..cdef1a2 100644
--- a/docs_src/p03-events.html.js
+++ b/docs_src/p03-events.html.js
@@ -113,6 +113,25 @@ export function page({ pkg, info }){
el(h3, "Final notes"),
el("p", "The library also provides a method to dispatch the events."),
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
+
+ el("div", { className: "notice" }).append(
+ el("p", "Mnemonic"),
+ el("ul").append(
+ el("li").append(
+ el("code", "on(, [, ])()"), " — just ", el("code", ".addEventListener(, [, ])")
+ ),
+ el("li").append(
+ el("code", "on.(, [, ])()"), " — corresponds to custom elemnets callbacks ", el("code", "Callback(...){...}"),
+ ". To connect to custom element see following page, else it is simulated by MutationObserver."
+ ),
+ el("li").append(
+ el("code", "dispatchEvent([, ])(element)"), " — just ", el("code", ".dispatchEvent(new Event([, ]))")
+ ),
+ el("li").append(
+ el("code", "dispatchEvent([, ])(element, detail)"), " — just ", el("code", ".dispatchEvent(new CustomEvent(, { detail, ... }))")
+ ),
+ )
+ ),
el(prevNext, info)
)
diff --git a/docs_src/p04-signals.html.js b/docs_src/p04-signals.html.js
index a3845ce..38442fc 100644
--- a/docs_src/p04-signals.html.js
+++ b/docs_src/p04-signals.html.js
@@ -14,107 +14,67 @@ export function page({ pkg, info }){
return el().append(
el(header, { info, pkg }),
el("main").append(
- el("h2", "Listenning to the native DOM events and other Addons"),
+ el("h2", "Using signals to manage reactivity"),
el("p").append(
- "We quickly introduce helper to listening to the native DOM events.",
- " ",
- "And library syntax/pattern so-called ", el("em", "Addon"), " to",
- " incorporate not only this in UI templates declaratively."
+ "How a program responds to variable data or user",
+ " interactions is one of the fundamental problems of programming.",
+ " If we desire to solve the issue in a declarative manner,",
+ " signals may be a viable approach.",
),
+ el(example, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
- el(h3, "Events and listenners"),
+ el(h3, "Introducing signals"),
el("p").append(
- "In JavaScript you can listen to the native DOM events of the given element by using ",
- el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener", title: "addEventListener on MDN" }).append(
- el("code", "element.addEventListener(type, listener, options)")
- ), ".",
- " ",
- "The library provides an alternative (", el("code", "on"), ") accepting the differen order",
- " of the arguments:"
+ "Using signals, we split program logic into the three parts.",
+ " Firstly (α), we create a variable (constant) representing reactive",
+ " value. Somewhere later, we can register (β) a logic reacting",
+ " to the signal value changes. Similarly, in a remaining part (γ), we",
+ " can update the signal value."
),
- el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
el("p").append(
- "…this is actually one of the two differences. The another one is that ", el("code", "on"),
- " accepts only object as the ", el("code", "options"), " (but it is still optional)."
+ "All this is just an example of ",
+ el("a", { textContent: "Event-driven programming", href: "https://en.wikipedia.org/wiki/Event-driven_programming", title: "Wikipedia: Event-driven programming" }),
+ " and ",
+ el("a", { textContent: "Publish–subscribe pattern", href: "https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern", title: "Wikipedia: Publish–subscribe pattern" }),
+ " (compare for example with ", el("a", { textContent: "fpubsub library", href: "https://www.npmjs.com/package/fpubsub", title: "NPM package: fpubsub" }), ").",
+ " All three parts can be in some manner independent and still connected",
+ " to the same reactive entity."
),
- el("p", { className: "notice" }).append(
- "The other difference is that there is ", el("strong", "no"), " ", el("code", "off"), " function.",
- " ",
- "You can remove listener declaratively using ", el("a", { textContent: "AbortSignal", href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal", title: "part of addEventListener on MDN" }),
- ":"
+ el("p").append(
+ "Signals are implemented in the library as functions. To see current value",
+ " of signal, just call it without any arguments ", el("code", "console.log(signal())"), ".",
+ " To update the signal value, pass any argument ", el("code", "signal('a new value')"), ".",
+ " For listenning the signal value changes, use ", el("code", "S.on(signal, console.log)"), "."
),
- el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
+ el("p").append(
+ "Similarly to the ", el("code", "on"), " function to register DOM events listener.",
+ " You can use ", el("code", "AbortController"), "/", el("code", "AbortSignal"), " to",
+ " ", el("em", "off"), "/stop listenning. For representing “live” piece of code computation pattern:"
+ ),
+ el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
el("div", { className: "notice" }).append(
- el("p", "So, there are (typically) three ways to handle events. You can use:"),
+ el("p", "Mnemonic"),
el("ul").append(
- el("li").append( el("code", `el("button", { textContent: "click me", "=onclick": "console.log(event)" })`)),
- el("li").append( el("code", `el("button", { textContent: "click me", onclick: console.log })`)),
- el("li").append( el("code", `el("button", { textContent: "click me" }, on("click", console.log))`))
- ),
- el("p").append(
- "In the first example we force to use HTML attribute (it corresponds to ", el("code", ``), ").",
- " ",
- el("em", "Side note: this can be useful in case of SSR."),
- " ",
- "To study difference, you can read a nice summary here: ", el("a", { href: "https://gist.github.com/WebReflection/b404c36f46371e3b1173bf5492acc944", textContent: "GIST @WebReflection/web_events.md" }), "."
+ el("li").append(
+ el("code", "S()"), " — signal: reactive value",
+ ),
+ el("li").append(
+ el("code", "S(()=> )"), " — signal: reactive value dependent on calculation using other signals",
+ ),
+ el("li").append(
+ el("code", "S.on(, [, ])"), " — listen to the signal value changes",
+ ),
+ el("li").append(
+ el("code", "S.clear(...)"), " — off and clear signals",
+ ),
+ el("li").append(
+ el("code", "S(, )"), " — signal: pattern to create complex reactive objects/arrays",
+ ),
+ el("li").append(
+ el("code", "S.action(, , ...)"), " — invoke an action for given signal"
+ )
)
),
-
- el(h3, "Addons"),
- el("p").append(
- "From practical point of view, ", el("em", "Addons"), " are just functions that accept any html element",
- " as their first parameter. You can see that the ", el("code", "on(…)"), " fullfills this requirement."
- ),
- el("p").append(
- "You can use Addons as ≥3rd argument of ", el("code", "el"), " function. This way is possible to extends",
- " you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:"
- ),
- el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
- el("p").append(
- "As the example shows, you can also provide types in JSDoc+TypeScript by using global type ", el("code", "ddeElementAddon"), ".",
- " ",
- "Also notice, you can use Addons to get element reference.",
- ),
- el(h3, "Life-cycle events"),
- el("p").append(
- "Addons are called immediately when the element is created, even it is not connected to live DOM yet.",
- " ",
- "Therefore, you can understand the Addon to be “oncreate” event."
- ),
- el("p").append(
- "The library provide three additional live-cycle events corresponding to how they are named in",
- " a case of custom elements: ", el("code", "on.connected"), ", ", el("code", "on.disconnected"),
- " and ", el("code", "on.attributeChanged"), "."
- ),
- el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
- el("p").append(
- "For Custom elements, we will later introduce a way to replace ", el("code", "*Callback"),
- " syntax with ", el("code", "dde:*"), " events. The ", el("code", "on.*"), " functions then",
- " listen to the appropriate Custom Elements events (see ", el("a", { textContent: "Custom element lifecycle callbacks | MDN", href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" }), ")."
- ),
- el("p").append(
- "But, in case of regular elemnets the ", el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" }).append(el("code", "MutationObserver"), " | MDN"),
- " is internaly used to track these events. Therefore, there are some drawbacks:",
- ),
- el("ul").append(
- el("li").append(
- "To proper listener registration, you need to use ", el("code", "on.*"), " not `on(\"dde:*\", …)`!"
- ),
- el("li").append(
- "Use sparingly! Internally, library must loop of all registered events and fires event properly.",
- " ",
- el("strong", "It is good practice to use the fact that if an element is removed, its children are also removed!"),
- " ",
- "In this spirit, we will introduce later the ", el("strong", "host"), " syntax to register",
- " clean up procedures when the component is removed from the app."
- ),
- ),
-
- el(h3, "Final notes"),
- el("p", "The library also provides a method to dispatch the events."),
- el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
-
- el(prevNext, info)
)
);
}
diff --git a/docs_src/ssr.js b/docs_src/ssr.js
index d869ca0..8af2919 100644
--- a/docs_src/ssr.js
+++ b/docs_src/ssr.js
@@ -6,6 +6,7 @@ export const pages= [
{ id: "index", href: "./", title: "Introduction", description: "Introducing a library." },
{ id: "p02-elements", href: "p02-elements", title: "Elements", description: "Basic concepts of elements modifications and creations." },
{ id: "p03-events", href: "p03-events", title: "Events and Addons", description: "Using not only events in UI declaratively." },
+ { id: "p04-signals", href: "p04-signals", title: "Signals and reactivity", description: "Handling reactivity in UI via signals." },
];
/**
* @typedef registerClientFile