diff --git a/docs/index.html b/docs/index.html index 2c6d70b..7f3dba8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,4 +1,4 @@ -
The library tries to provide pure JavaScript tool(s) to create reactive interfaces.The main goals are:
It is, in fact, an reimplementation of jaandrle/dollar_dom_component.
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+`deka-dom-el` — Introduction/Guide `deka-dom-el` — Introduction/Guide
The library tries to provide pure JavaScript tool(s) to create reactive interfaces. The main goals are:
- provide a small wrappers/utilities around the native JavaScript DOM
- keep library size around 10kB at maximum (if possible)
- zero dependencies (if possible)
- prefer a declarative/functional approach
- unopinionated (allow alternative methods)
It is, in fact, an reimplementation of jaandrle/dollar_dom_component.
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const clicks= S(0);
document.body.append(
el("<>").append(
@@ -12,4 +12,106 @@ document.body.append(
})
)
);
-
\ No newline at end of file
+
Let’s go through all patterns we would like to use and what needs to be improved for better experience.
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.
document.body.append(
+ document.createElement("div")
+);
+console.log(
+ "Emty div is generated inside <body>:",
+ document.body.innerHTML.includes("<div></div>")
+);
+
+document.body.append(
+ Object.assign(
+ document.createElement("p"),
+ { textContent: "Element’s text content.", style: "color: coral;" }
+ )
+);
+
To make this easier, you can use the el
function. Internally in basic examples, it is wrapper around assign(document.createElement(…), { … })
.
import { el, assign } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+const color= "lightcoral";
+document.body.append(
+ el("p", { textContent: "Hello (first time)", style: { color } })
+);
+document.body.append(
+ assign(
+ document.createElement("p"),
+ { textContent: "Hello (second time)", style: { color } }
+ )
+);
+
The assign
function provides improved behaviour of Object.assign()
. You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the element.setAttribute
if there is no writable property in the element prototype.
You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from HTMLElement. To see all available IDLs for example for paragraphs, see HTMLParagraphElement. Moreover, the assign
provides a way to sets declaratively some convenient properties:
data-*
/aria-*
attributes using object notation.data-*
/aria-*
attribute using camelCase notation.style
property.className
(IDL – preffered)/class
are ways to add CSS class to the element. You can use string (similarly to class="…"
syntax in HTML) or array of strings. This is handy to concat conditional classes.classList
to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.assign
also accepts the undefined
as a value for any property to remove it from the element declaratively. Also for some IDL the corresponding attribute is removed as it can be confusing. For example, natievly the element’s id
is removed by setting the IDL to an empty string.For processing, the assign
internally uses assignAttribute
and classListDeclarative
.
import { assignAttribute, classListDeclarative } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+const paragraph= document.createElement("p");
+
+assignAttribute(paragraph, "textContent", "Hello, world!");
+assignAttribute(paragraph, "style", { color: "navy" });
+
+assignAttribute(paragraph, "dataTest1", "v1");
+assignAttribute(paragraph, "dataset", { test2: "v2" });
+
+assignAttribute(paragraph, "ariaLabel", "v1");
+assignAttribute(paragraph, "ariaset", { role: "none" });
+
+
+classListDeclarative(paragraph, {
+ classAdd: true,
+ classRemove: false,
+ classAdd1: 1,
+ classRemove1: 0,
+ classToggle: -1
+});
+
+console.log(paragraph.outerHTML);
+document.body.append(
+ paragraph
+);
+
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")
+);
+console.log(document.body.innerHTML.includes("<div></div><span></span><main></main>"));
+const template= document.createElement("main").append(
+ document.createElement("div"),
+ document.createElement("span"),
+);
+console.log(typeof template==="undefined");
+
This library therefore ooverwrites the append
method to always return parent element.
import { el } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
+document.head.append(
+ el("style").append(
+ "tr, td{ border: 1px solid red; padding: 1em; }",
+ "table{ border-collapse: collapse; }"
+ )
+);
+document.body.append(
+ el("p", "Example of a complex template. Using for example nesting lists:"),
+ el("ul").append(
+ el("li", "List item 1"),
+ el("li").append(
+ el("ul").append(
+ el("li", "Nested list item 1"),
+ )
+ )
+ ),
+ el("table").append(
+ el("tr").append(
+ el("td", "Row 1 – Col 1"),
+ el("td", "Row 1 – Col 2")
+ )
+ )
+);
+
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; }",
+ ".class2{ color: purple; }"
+ )
+);
+document.body.append(
+ el(component, { className: "class2", textContent: "Hello World!" }),
+ component({ className: "class2", textContent: "Hello World!" })
+);
+
+function component({ className, textContent }){
+ return el("div", { className: [ "class1", className ] }).append(
+ el("p", textContent)
+ );
+}
+
It is nice to use similar naming convention as native DOM API.
\ No newline at end of file diff --git a/docs_src/components/example.html.js b/docs_src/components/example.html.js index c2feacb..e066e17 100644 --- a/docs_src/components/example.html.js +++ b/docs_src/components/example.html.js @@ -1,4 +1,4 @@ -let is_register= false; +let is_registered= false; import { styles } from "../index.css.js"; export const css= styles().scope(example).css` :host{ @@ -18,10 +18,7 @@ export function example({ src, language= "javascript" }){ return el("<>").append( el("div", { id, className: example.name }).append( el("pre").append( - el("code", { - className: "language-"+language, - textContent: code - }) + el("code", { className: "language-"+language, textContent: code }) ) ), elCode({ id, content: code }) @@ -35,9 +32,9 @@ function elCode({ id, content }){ return el("script", `Flems(document.getElementById("${id}"), JSON.parse(${JSON.stringify(options)}));`); } function register(){ - if(is_register) return; + if(is_registered) return; document.head.append( el("script", { src: "https://flems.io/flems.html", type: "text/javascript", charset: "utf-8" }) ); - is_register= true; + is_registered= true; } diff --git a/docs_src/components/examples/dekaAppend.js b/docs_src/components/examples/dekaAppend.js new file mode 100644 index 0000000..967242e --- /dev/null +++ b/docs_src/components/examples/dekaAppend.js @@ -0,0 +1,24 @@ +import { el } from "../../../index-with-signals.js"; +document.head.append( + el("style").append( + "tr, td{ border: 1px solid red; padding: 1em; }", + "table{ border-collapse: collapse; }" + ) +); +document.body.append( + el("p", "Example of a complex template. Using for example nesting lists:"), + el("ul").append( + el("li", "List item 1"), + el("li").append( + el("ul").append( + el("li", "Nested list item 1"), + ) + ) + ), + el("table").append( + el("tr").append( + el("td", "Row 1 – Col 1"), + el("td", "Row 1 – Col 2") + ) + ) +); diff --git a/docs_src/components/examples/dekaAssign.js b/docs_src/components/examples/dekaAssign.js new file mode 100644 index 0000000..a9896c2 --- /dev/null +++ b/docs_src/components/examples/dekaAssign.js @@ -0,0 +1,25 @@ +import { assignAttribute, classListDeclarative } from "../../../index-with-signals.js"; +const paragraph= document.createElement("p"); + +assignAttribute(paragraph, "textContent", "Hello, world!"); +assignAttribute(paragraph, "style", { color: "navy" }); + +assignAttribute(paragraph, "dataTest1", "v1"); +assignAttribute(paragraph, "dataset", { test2: "v2" }); + +assignAttribute(paragraph, "ariaLabel", "v1"); +assignAttribute(paragraph, "ariaset", { role: "none" }); + + +classListDeclarative(paragraph, { + classAdd: true, + classRemove: false, + classAdd1: 1, + classRemove1: 0, + classToggle: -1 +}); + +console.log(paragraph.outerHTML); +document.body.append( + paragraph +); diff --git a/docs_src/components/examples/dekaBasicComponent.js b/docs_src/components/examples/dekaBasicComponent.js new file mode 100644 index 0000000..7e742fa --- /dev/null +++ b/docs_src/components/examples/dekaBasicComponent.js @@ -0,0 +1,17 @@ +import { el } from "../../../index-with-signals.js"; +document.head.append( + el("style").append( + ".class1{ font-weight: bold; }", + ".class2{ color: purple; }" + ) +); +document.body.append( + el(component, { className: "class2", textContent: "Hello World!" }), + component({ className: "class2", textContent: "Hello World!" }) +); + +function component({ className, textContent }){ + return el("div", { className: [ "class1", className ] }).append( + el("p", textContent) + ); +} diff --git a/docs_src/components/examples/dekaCreateElement.js b/docs_src/components/examples/dekaCreateElement.js new file mode 100644 index 0000000..8b8d584 --- /dev/null +++ b/docs_src/components/examples/dekaCreateElement.js @@ -0,0 +1,11 @@ +import { el, assign } from "../../../index-with-signals.js"; +const color= "lightcoral"; +document.body.append( + el("p", { textContent: "Hello (first time)", style: { color } }) +); +document.body.append( + assign( + document.createElement("p"), + { textContent: "Hello (second time)", style: { color } } + ) +); diff --git a/docs_src/components/examples/nativeAppend.js b/docs_src/components/examples/nativeAppend.js new file mode 100644 index 0000000..5313044 --- /dev/null +++ b/docs_src/components/examples/nativeAppend.js @@ -0,0 +1,11 @@ +document.body.append( + document.createElement("div"), + document.createElement("span"), + document.createElement("main") +); +console.log(document.body.innerHTML.includes("")); +const template= document.createElement("main").append( + document.createElement("div"), + document.createElement("span"), +); +console.log(typeof template==="undefined"); diff --git a/docs_src/components/examples/nativeCreateElement.js b/docs_src/components/examples/nativeCreateElement.js new file mode 100644 index 0000000..0757fff --- /dev/null +++ b/docs_src/components/examples/nativeCreateElement.js @@ -0,0 +1,14 @@ +document.body.append( + document.createElement("div") +); +console.log( + "Emty div is generated inside :", + document.body.innerHTML.includes("") +); + +document.body.append( + Object.assign( + document.createElement("p"), + { textContent: "Element’s text content.", style: "color: coral;" } + ) +); diff --git a/docs_src/index.html.js b/docs_src/index.html.js index 183a6bf..35cd8cf 100644 --- a/docs_src/index.html.js +++ b/docs_src/index.html.js @@ -24,12 +24,12 @@ export function body(pkg){ return el("<>").append( el("h1", pageName(pkg)), el("p").append( - "The library tries to provide pure JavaScript tool(s) to create reactive interfaces.", + "The library tries to provide pure JavaScript tool(s) to create reactive interfaces. ", "The main goals are:" ), el("ul").append( - el("li", "provide a small wrapper around the native JavaScript DOM"), - el("li", "keep library size under 10kB"), + el("li", "provide a small wrappers/utilities around the native JavaScript DOM"), + el("li", "keep library size around 10kB at maximum (if possible)"), el("li", "zero dependencies (if possible)"), el("li", "prefer a declarative/functional approach"), el("li", "unopinionated (allow alternative methods)"), @@ -43,7 +43,94 @@ export function body(pkg){ }), ".", ), - el(example, { src: "./components/examples/helloWorld.js" }) + el(example, { src: "./components/examples/helloWorld.js" }), + + + el("h2", "Native JavaScript DOM elements creations"), + el("p", "Let’s go through all patterns we would like to use and what needs to be improved for better experience."), + + el("h3", "Creating element(s) (with custom attributes)"), + el("p").append( + "You can create a native DOM element by using the ", + el("a", { href: "https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement", title: "MDN documentation for document.createElement()" }).append( + el("code", "document.createElement()") + ), ". ", + "It is also possible to provide a some attribute(s) declaratively using ", el("code", "Object.assign()"), ". ", + "More precisely, this way you can sets some ", + el("a", { + href: "https://developer.mozilla.org/en-US/docs/Glossary/IDL", + title: "MDN page about Interface Description Language" + }).append( + el("abbr", { textContent: "IDL", title: "Interface Description Language" }) + ), "." + ), + el(example, { src: "./components/examples/nativeCreateElement.js" }), + el("p").append( + "To make this easier, you can use the ", el("code", "el"), " function. ", + "Internally in basic examples, it is wrapper around ", el("code", "assign(document.createElement(…), { … })"), "." + ), + el(example, { src: "./components/examples/dekaCreateElement.js" }), + el("p").append( + "The ", el("code", "assign"), " function provides improved behaviour of ", el("code", "Object.assign()"), ". ", + "You can declaratively sets any IDL and attribute of the given element. ", + "Function prefers IDL and fallback to the ", el("code", "element.setAttribute"), " if there is no writable property in the element prototype." + ), + el("p").append( + "You can study all JavaScript elements interfaces to the corresponding HTML elements. ", + "All HTML elements inherits from ", el("a", { textContent: "HTMLElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement" }), ". ", + "To see all available IDLs for example for paragraphs, see ", el("a", { textContent: "HTMLParagraphElement", href: "https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement" }), ". ", + "Moreover, the ", el("code", "assign"), " provides a way to sets declaratively some convenient properties:" + ), + el("ul").append( + el("li").append( + "It is possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attributes using object notation." + ), + el("li").append( + "In opposite, it is also possible to sets ", el("code", "data-*"), "/", el("code", "aria-*"), " attribute using camelCase notation." + ), + el("li").append( + "You can use string or object as a value for ", el("code", "style"), " property." + ), + el("li").append( + el("code", "className"), " (IDL – preffered)/", el("code", "class"), " are ways to add CSS class to the element. ", + "You can use string (similarly to ", el("code", "class=\"…\"") ," syntax in HTML) or array of strings. ", + "This is handy to concat conditional classes." + ), + el("li").append( + "Use ", el("code", "classList"), " to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.", + ), + el("li").append( + "The ", el("code", "assign"), " also accepts the ", el("code", "undefined"), " as a value for any property to remove it from the element declaratively. ", + "Also for some IDL the corresponding attribute is removed as it can be confusing. ", + el("em").append( + "For example, natievly the element’s ", el("code", "id"), " is removed by setting the IDL to an empty string." + ) + ) + ), + el("p").append( + "For processing, the ", el("code", "assign"), " internally uses ", el("code", "assignAttribute"), " and ", el("code", "classListDeclarative"), "." + ), + el(example, { src: "./components/examples/dekaAssign.js" }), + + el("h3", "Native JavaScript templating"), + el("p", "By default, the native JS has no good way to define HTML template using DOM API:"), + el(example, { src: "./components/examples/nativeAppend.js" }), + el("p").append( + "This library therefore ooverwrites the ", el("code", "append"), " method to always return parent element." + ), + el(example, { src: "./components/examples/dekaAppend.js" }), + + + el("h2", "Creating advanced (reactive) templates and components"), + + el("h3", "Basic components"), + el("p").append( + "You can use functions for encapsulation (repeating) logic. ", + "The ", el("code", "el"), " accepts function as first argument. ", + "In that case, the function should return dom elements and the second argument for ", el("code", "el"), " is argument for given element." + ), + el(example, { src: "./components/examples/dekaBasicComponent.js" }), + el("p", "It is nice to use similar naming convention as native DOM API.") ); } function pageName(pkg){