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 observables (O
) and how them incorporate to UI templates.
Now we will clarify how the observables 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-observables.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 observables (O
) and how them incorporate to UI templates.
Now we will clarify how the observables 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-observables.js";
import { O } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
const clicks= O(0);
document.body.append(
diff --git a/docs/p02-elements.html b/docs/p02-elements.html
index 5f8e9ea..ed6b19e 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.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
+`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.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
import {
assign,
el, createElement,
diff --git a/docs/p03-events.html b/docs/p03-events.html
index eeeb376..55f7ded 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.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.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.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm.js
import { on, dispatchEvent } from "deka-dom-el";
/** @type {ddeElementAddon} */
diff --git a/docs/p04-observables.html b/docs/p04-observables.html
index ce28451..85f730a 100644
--- a/docs/p04-observables.html
+++ b/docs/p04-observables.html
@@ -1,4 +1,4 @@
-`deka-dom-el` — Observables and reactivity `deka-dom-el` — Observables and reactivity
Handling reactivity in UI via observables.
Using observables 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, observables may be a viable approach.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
+`deka-dom-el` — Observables and reactivity `deka-dom-el` — Observables and reactivity
Handling reactivity in UI via observables.
Using observables 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, observables may be a viable approach.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
import { O, observable } from "deka-dom-el/observables";
O===observable
/** @type {ddeObservable} */
@@ -147,4 +147,4 @@ setTimeout(clearInterval, 10*interval, setInterval(function(){
O.action(count, "add");
O.action(numbers, "push", count());
}, interval));
-
# Mnemonic
O(<value>)
— observable: reactive valueO(()=> <computation>)
— observable: reactive value dependent on calculation using other observablesO.on(<observable>, <listener>[, <options>])
— listen to the observable value changesO.clear(...<observables>)
— off and clear observablesO(<value>, <actions>)
— observable: pattern to create complex reactive objects/arraysO.action(<observable>, <action-name>, ...<action-arguments>)
— invoke an action for given observableO.el(<observable>, <function-returning-dom>)
— render partial dom structure (template) based on the current observable value
\ No newline at end of file
+
# Mnemonic
O(<value>)
— observable: reactive valueO(()=> <computation>)
— observable: reactive value dependent on calculation using other observablesO.on(<observable>, <listener>[, <options>])
— listen to the observable value changesO.clear(...<observables>)
— off and clear observablesO(<value>, <actions>)
— observable: pattern to create complex reactive objects/arraysO.action(<observable>, <action-name>, ...<action-arguments>)
— invoke an action for given observableO.el(<observable>, <function-returning-dom>)
— render partial dom structure (template) based on the current observable value
\ No newline at end of file
diff --git a/docs/p05-scopes.html b/docs/p05-scopes.html
new file mode 100644
index 0000000..2ab2fa7
--- /dev/null
+++ b/docs/p05-scopes.html
@@ -0,0 +1,78 @@
+`deka-dom-el` — Scopes and components `deka-dom-el` — Scopes and components
Organizing UI into components
Using functions as UI components
For state-less components we can use functions as UI components (see “Elements” page). But in real life, we may need to handle the component live-cycle and provide JavaScript the way to properly use the Garbage collection.
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
+import { scope, el, on } from "deka-dom-el";
+/** @type {ddeElementAddon} */
+
The library therefore use scopes to provide these functionalities.
# Scopes and hosts
The host is the name for the element representing the component. This is typically element returned by function. To get reference, you can use scope.host()
to applly addons just use scope.host(...<addons>)
.
import { el, on, scope } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
+const { host }= scope;
+host(
+ element=> console.log(
+ "This represents Addon/oninit for root",
+ element.outerHTML
+ )
+);
+console.log(
+ "This represents the reference to the host element of root",
+ host().outerHTML
+);
+document.body.append(
+ el(component)
+);
+function component(){
+ const { host }= scope;
+ host(
+ element=> console.log(
+ "This represents Addon/oninit for the component",
+ element.outerHTML
+ )
+ );
+ const onclick= on("click", function(ev){
+ console.log(
+ "This represents the reference to the host element of the component",
+ host().outerHTML
+ );
+ })
+ return el("div", null, onclick).append(
+ el("strong", "Component")
+ );
+}
+
To better understanding we implement function elClass
helping to create component as class instances.
import { chainableAppend, el, scope } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js";
+class Test {
+ constructor(params){
+ this._params= params;
+ }
+ render(){
+ return el("div").append(
+ this._params.textContent
+ );
+ }
+}
+document.body.append(
+ elClass(Test, { textContent: "Hello World" })
+);
+
+function elClass(c, props, ...addons){
+ let element, element_host;
+ scope.push({
+ scope: c, //just informative purposes
+ host: (...c)=> c.length
+ ? (!element
+ ? addons.unshift(...c)
+ : c.forEach(c=> c(element_host)), undefined)
+ : element_host
+ });
+ const C= new c(props);
+ element= C.render();
+ const is_fragment= el instanceof DocumentFragment;
+ const el_mark= el.mark({ //this creates html comment `<dde:mark …/>`
+ type: "class-component",
+ name: C.name,
+ host: is_fragment ? "this" : "parentElement",
+ });
+ element.prepend(el_mark);
+ if(is_fragment) element_host= el_mark;
+
+ chainableAppend(element);
+ addons.forEach(c=> c(element_host));
+ scope.pop();
+ return element;
+}
+
# Mnemonic
el(<function>, <function-argument(s)>)[.append(...)]: <element-returned-by-function>
— using component represented by functionscope.host()
— get current component referencescope.host(...<addons>)
— use addons to current component
\ No newline at end of file
diff --git a/docs_src/components/examples/scopes/class-component.js b/docs_src/components/examples/scopes/class-component.js
new file mode 100644
index 0000000..5e88740
--- /dev/null
+++ b/docs_src/components/examples/scopes/class-component.js
@@ -0,0 +1,41 @@
+import { chainableAppend, el, scope } from "deka-dom-el";
+class Test {
+ constructor(params){
+ this._params= params;
+ }
+ render(){
+ return el("div").append(
+ this._params.textContent
+ );
+ }
+}
+document.body.append(
+ elClass(Test, { textContent: "Hello World" })
+);
+
+function elClass(c, props, ...addons){
+ let element, element_host;
+ scope.push({
+ scope: c, //just informative purposes
+ host: (...c)=> c.length
+ ? (!element
+ ? addons.unshift(...c)
+ : c.forEach(c=> c(element_host)), undefined)
+ : element_host
+ });
+ const C= new c(props);
+ element= C.render();
+ const is_fragment= el instanceof DocumentFragment;
+ const el_mark= el.mark({ //this creates html comment ` `
+ type: "class-component",
+ name: C.name,
+ host: is_fragment ? "this" : "parentElement",
+ });
+ element.prepend(el_mark);
+ if(is_fragment) element_host= el_mark;
+
+ chainableAppend(element);
+ addons.forEach(c=> c(element_host));
+ scope.pop();
+ return element;
+}
diff --git a/docs_src/components/examples/scopes/intro.js b/docs_src/components/examples/scopes/intro.js
new file mode 100644
index 0000000..d0f1ab0
--- /dev/null
+++ b/docs_src/components/examples/scopes/intro.js
@@ -0,0 +1,3 @@
+// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-observables.js
+import { scope, el, on } from "deka-dom-el";
+/** @type {ddeElementAddon} */
diff --git a/docs_src/components/examples/scopes/scopes-and-hosts.js b/docs_src/components/examples/scopes/scopes-and-hosts.js
new file mode 100644
index 0000000..115816e
--- /dev/null
+++ b/docs_src/components/examples/scopes/scopes-and-hosts.js
@@ -0,0 +1,33 @@
+import { el, on, scope } from "deka-dom-el";
+const { host }= scope;
+host(
+ element=> console.log(
+ "This represents Addon/oninit for root",
+ element.outerHTML
+ )
+);
+console.log(
+ "This represents the reference to the host element of root",
+ host().outerHTML
+);
+document.body.append(
+ el(component)
+);
+function component(){
+ const { host }= scope;
+ host(
+ element=> console.log(
+ "This represents Addon/oninit for the component",
+ element.outerHTML
+ )
+ );
+ const onclick= on("click", function(ev){
+ console.log(
+ "This represents the reference to the host element of the component",
+ host().outerHTML
+ );
+ })
+ return el("div", null, onclick).append(
+ el("strong", "Component")
+ );
+}
diff --git a/docs_src/components/mnemonic/scopes-init.js b/docs_src/components/mnemonic/scopes-init.js
new file mode 100644
index 0000000..61ff544
--- /dev/null
+++ b/docs_src/components/mnemonic/scopes-init.js
@@ -0,0 +1,16 @@
+import { el } from "deka-dom-el";
+import { mnemonicUl } from "../mnemonicUl.html.js";
+
+export function mnemonic(){
+ return mnemonicUl().append(
+ el("li").append(
+ el("code", "el(, )[.append(...)]: "), " — using component represented by function",
+ ),
+ el("li").append(
+ el("code", "scope.host()"), " — get current component reference"
+ ),
+ el("li").append(
+ el("code", "scope.host(...)"), " — use addons to current component",
+ )
+ );
+}
diff --git a/docs_src/p05-scopes.html.js b/docs_src/p05-scopes.html.js
new file mode 100644
index 0000000..7a48be0
--- /dev/null
+++ b/docs_src/p05-scopes.html.js
@@ -0,0 +1,41 @@
+import { simplePage } from "./layout/simplePage.html.js";
+
+import { el } from "deka-dom-el";
+import { example } from "./components/example.html.js";
+import { h3 } from "./components/pageUtils.html.js";
+import { mnemonic } from "./components/mnemonic/scopes-init.js";
+import { code } from "./components/code.html.js";
+/** @param {string} url */
+const fileURL= url=> new URL(url, import.meta.url);
+
+/** @param {import("./types.d.ts").PageAttrs} attrs */
+export function page({ pkg, info }){
+ const page_id= info.id;
+ return el(simplePage, { info, pkg }).append(
+ el("h2", "Using functions as UI components"),
+ el("p").append(
+ "For state-less components we can use functions as UI components (see “Elements” page).",
+ " But in real life, we may need to handle the component live-cycle and provide",
+ " JavaScript the way to properly use the ", el("a", { textContent: "Garbage collection", href: "https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection", title: "Garbage collection | MDN" }), "."
+ ),
+ el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
+ el("p").append(
+ "The library therefore use ", el("em", "scopes"), " to provide these functionalities.",
+ ),
+
+ el(h3, "Scopes and hosts"),
+ el("p").append(
+ "The ", el("strong", "host"), " is the name for the element representing the component.",
+ " This is typically element returned by function. To get reference, you can use ",
+ el("code", "scope.host()"), " to applly addons just use ", el("code", "scope.host(...)"), "."
+ ),
+ el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
+ el("p").append(
+ "To better understanding we implement function ", el("code", "elClass"), " helping to create",
+ " component as class instances."
+ ),
+ el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
+
+ el(mnemonic)
+ );
+}
diff --git a/docs_src/ssr.js b/docs_src/ssr.js
index cb5278c..817bbaa 100644
--- a/docs_src/ssr.js
+++ b/docs_src/ssr.js
@@ -7,6 +7,7 @@ export const pages= [
{ 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-observables", href: "p04-observables", title: "Observables and reactivity", description: "Handling reactivity in UI via observables." },
+ { id: "p05-scopes", href: "p05-scopes", title: "Scopes and components", description: "Organizing UI into components" },
];
/**
* @typedef registerClientFile