mirror of
https://github.com/jaandrle/deka-dom-el
synced 2024-11-21 23:39:37 +01:00
117 lines
15 KiB
HTML
117 lines
15 KiB
HTML
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="index.css"><meta name="description" content="Introducing basic concepts."><!--<dde:mark type="component" name="metaTwitter"/>--><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="twitter:title" content="deka-dom-el"><meta name="twitter:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="twitter:creator" content="@jaandrle"><!--<dde:mark type="component" name="metaFacebook"/>--><meta name="og:url" content="https://github.com/jaandrle/deka-dom-el"><meta name="og:title" content="deka-dom-el"><meta name="og:description" content="A low-code library that simplifies the creation of native DOM elements/components using small wrappers and tweaks."><meta name="og:creator" content="@jaandrle"><title>`deka-dom-el` — Introduction/Guide</title><script src="https://flems.io/flems.html" type="text/javascript" charset="utf-8"></script></head><body><!--<dde:mark type="component" name="body"/>--><h1>`deka-dom-el` — Introduction/Guide</h1><p>The library tries to provide pure JavaScript tool(s) to create reactive interfaces. The main goals are:</p><ul><li>provide a small wrappers/utilities around the native JavaScript DOM</li><li>keep library size around 10kB at maximum (if possible)</li><li>zero dependencies (if possible)</li><li>prefer a declarative/functional approach</li><li>unopinionated (allow alternative methods)</li></ul><p class="note">It is, in fact, an reimplementation of <a href="https://github.com/jaandrle/dollar_dom_component" title="GitHub repository of library. Motto: Functional DOM components without JSX and virtual DOM.">jaandrle/dollar_dom_component</a>.</p><!--<dde:mark type="component" name="example"/>--><div id="code-uoifx" class="example"><pre><code class="language-javascript">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(
|
||
el("p", S(()=>
|
||
"Hello World "+"🎉".repeat(clicks())
|
||
)),
|
||
el("button", {
|
||
type: "button",
|
||
onclick: ()=> clicks(clicks()+1),
|
||
textContent: "Fire"
|
||
})
|
||
)
|
||
);
|
||
</code></pre></div><script>Flems(document.getElementById("code-uoifx"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, S } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst clicks= S(0);\\ndocument.body.append(\\n\\tel(\\\"<>\\\").append(\\n\\t\\tel(\\\"p\\\", S(()=>\\n\\t\\t\\t\\\"Hello World \\\"+\\\"🎉\\\".repeat(clicks())\\n\\t\\t)),\\n\\t\\tel(\\\"button\\\", {\\n\\t\\t\\ttype: \\\"button\\\",\\n\\t\\t\\tonclick: ()=> clicks(clicks()+1),\\n\\t\\t\\ttextContent: \\\"Fire\\\"\\n\\t\\t})\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><h2>Native JavaScript DOM elements creations</h2><p>Let’s go through all patterns we would like to use and what needs to be improved for better experience.</p><h3>Creating element(s) (with custom attributes)</h3><p>You can create a native DOM element by using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement" title="MDN documentation for document.createElement()"><code>document.createElement()</code></a>. It is also possible to provide a some attribute(s) declaratively using <code>Object.assign()</code>. More precisely, this way you can sets some <a href="https://developer.mozilla.org/en-US/docs/Glossary/IDL" title="MDN page about Interface Description Language"><abbr title="Interface Description Language">IDL</abbr></a>.</p><!--<dde:mark type="component" name="example"/>--><div id="code-unfu2" class="example"><pre><code class="language-javascript">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;" }
|
||
)
|
||
);
|
||
</code></pre></div><script>Flems(document.getElementById("code-unfu2"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"document.body.append(\\n\\tdocument.createElement(\\\"div\\\")\\n);\\nconsole.log(\\n\\t\\\"Emty div is generated inside <body>:\\\",\\n\\tdocument.body.innerHTML.includes(\\\"<div></div>\\\")\\n);\\n\\ndocument.body.append(\\n\\tObject.assign(\\n\\t\\tdocument.createElement(\\\"p\\\"),\\n\\t\\t{ textContent: \\\"Element’s text content.\\\", style: \\\"color: coral;\\\" }\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><p>To make this easier, you can use the <code>el</code> function. Internally in basic examples, it is wrapper around <code>assign(document.createElement(…), { … })</code>.</p><!--<dde:mark type="component" name="example"/>--><div id="code-5zakj" class="example"><pre><code class="language-javascript">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 } }
|
||
)
|
||
);
|
||
</code></pre></div><script>Flems(document.getElementById("code-5zakj"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el, assign } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst color= \\\"lightcoral\\\";\\ndocument.body.append(\\n\\tel(\\\"p\\\", { textContent: \\\"Hello (first time)\\\", style: { color } })\\n);\\ndocument.body.append(\\n\\tassign(\\n\\t\\tdocument.createElement(\\\"p\\\"),\\n\\t\\t{ textContent: \\\"Hello (second time)\\\", style: { color } }\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><p>The <code>assign</code> function provides improved behaviour of <code>Object.assign()</code>. You can declaratively sets any IDL and attribute of the given element. Function prefers IDL and fallback to the <code>element.setAttribute</code> if there is no writable property in the element prototype.</p><p>You can study all JavaScript elements interfaces to the corresponding HTML elements. All HTML elements inherits from <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement">HTMLElement</a>. To see all available IDLs for example for paragraphs, see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement">HTMLParagraphElement</a>. Moreover, the <code>assign</code> provides a way to sets declaratively some convenient properties:</p><ul><li>It is possible to sets <code>data-*</code>/<code>aria-*</code> attributes using object notation.</li><li>In opposite, it is also possible to sets <code>data-*</code>/<code>aria-*</code> attribute using camelCase notation.</li><li>You can use string or object as a value for <code>style</code> property.</li><li><code>className</code> (IDL – preffered)/<code>class</code> are ways to add CSS class to the element. You can use string (similarly to <code>class="…"</code> syntax in HTML) or array of strings. This is handy to concat conditional classes.</li><li>Use <code>classList</code> to toggle specific classes. This will be handy later when the reactivity via signals is beeing introduced.</li><li>The <code>assign</code> also accepts the <code>undefined</code> 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. <em>For example, natievly the element’s <code>id</code> is removed by setting the IDL to an empty string.</em></li></ul><p>For processing, the <code>assign</code> internally uses <code>assignAttribute</code> and <code>classListDeclarative</code>.</p><!--<dde:mark type="component" name="example"/>--><div id="code-y8a12" class="example"><pre><code class="language-javascript">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
|
||
);
|
||
</code></pre></div><script>Flems(document.getElementById("code-y8a12"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { assignAttribute, classListDeclarative } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\nconst paragraph= document.createElement(\\\"p\\\");\\n\\nassignAttribute(paragraph, \\\"textContent\\\", \\\"Hello, world!\\\");\\nassignAttribute(paragraph, \\\"style\\\", { color: \\\"navy\\\" });\\n\\nassignAttribute(paragraph, \\\"dataTest1\\\", \\\"v1\\\");\\nassignAttribute(paragraph, \\\"dataset\\\", { test2: \\\"v2\\\" });\\n\\nassignAttribute(paragraph, \\\"ariaLabel\\\", \\\"v1\\\");\\nassignAttribute(paragraph, \\\"ariaset\\\", { role: \\\"none\\\" });\\n\\n\\nclassListDeclarative(paragraph, {\\n\\tclassAdd: true,\\n\\tclassRemove: false,\\n\\tclassAdd1: 1,\\n\\tclassRemove1: 0,\\n\\tclassToggle: -1\\n});\\n\\nconsole.log(paragraph.outerHTML);\\ndocument.body.append(\\n\\tparagraph\\n);\\n\"}],\"toolbar\":false}"));</script><h3>Native JavaScript templating</h3><p>By default, the native JS has no good way to define HTML template using DOM API:</p><!--<dde:mark type="component" name="example"/>--><div id="code-bfbe5" class="example"><pre><code class="language-javascript">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");
|
||
</code></pre></div><script>Flems(document.getElementById("code-bfbe5"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"document.body.append(\\n\\tdocument.createElement(\\\"div\\\"),\\n\\tdocument.createElement(\\\"span\\\"),\\n\\tdocument.createElement(\\\"main\\\")\\n);\\nconsole.log(document.body.innerHTML.includes(\\\"<div></div><span></span><main></main>\\\"));\\nconst template= document.createElement(\\\"main\\\").append(\\n\\tdocument.createElement(\\\"div\\\"),\\n\\tdocument.createElement(\\\"span\\\"),\\n);\\nconsole.log(typeof template===\\\"undefined\\\");\\n\"}],\"toolbar\":false}"));</script><p>This library therefore ooverwrites the <code>append</code> method to always return parent element.</p><!--<dde:mark type="component" name="example"/>--><div id="code-frx3p" class="example"><pre><code class="language-javascript">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")
|
||
)
|
||
)
|
||
);
|
||
</code></pre></div><script>Flems(document.getElementById("code-frx3p"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\ndocument.head.append(\\n\\tel(\\\"style\\\").append(\\n\\t\\t\\\"tr, td{ border: 1px solid red; padding: 1em; }\\\",\\n\\t\\t\\\"table{ border-collapse: collapse; }\\\"\\n\\t)\\n);\\ndocument.body.append(\\n\\tel(\\\"p\\\", \\\"Example of a complex template. Using for example nesting lists:\\\"),\\n\\tel(\\\"ul\\\").append(\\n\\t\\tel(\\\"li\\\", \\\"List item 1\\\"),\\n\\t\\tel(\\\"li\\\").append(\\n\\t\\t\\tel(\\\"ul\\\").append(\\n\\t\\t\\t\\tel(\\\"li\\\", \\\"Nested list item 1\\\"),\\n\\t\\t\\t)\\n\\t\\t)\\n\\t),\\n\\tel(\\\"table\\\").append(\\n\\t\\tel(\\\"tr\\\").append(\\n\\t\\t\\tel(\\\"td\\\", \\\"Row 1 – Col 1\\\"),\\n\\t\\t\\tel(\\\"td\\\", \\\"Row 1 – Col 2\\\")\\n\\t\\t)\\n\\t)\\n);\\n\"}],\"toolbar\":false}"));</script><h2>Creating advanced (reactive) templates and components</h2><h3>Basic components</h3><p>You can use functions for encapsulation (repeating) logic. The <code>el</code> accepts function as first argument. In that case, the function should return dom elements and the second argument for <code>el</code> is argument for given element.</p><!--<dde:mark type="component" name="example"/>--><div id="code-xyr7h" class="example"><pre><code class="language-javascript">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)
|
||
);
|
||
}
|
||
</code></pre></div><script>Flems(document.getElementById("code-xyr7h"), JSON.parse("{\"files\":[{\"name\":\".js\",\"content\":\"import { el } from \\\"https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js\\\";\\ndocument.head.append(\\n\\tel(\\\"style\\\").append(\\n\\t\\t\\\".class1{ font-weight: bold; }\\\",\\n\\t\\t\\\".class2{ color: purple; }\\\"\\n\\t)\\n);\\ndocument.body.append(\\n\\tel(component, { className: \\\"class2\\\", textContent: \\\"Hello World!\\\" }),\\n\\tcomponent({ className: \\\"class2\\\", textContent: \\\"Hello World!\\\" })\\n);\\n\\nfunction component({ className, textContent }){\\n\\treturn el(\\\"div\\\", { className: [ \\\"class1\\\", className ] }).append(\\n\\t\\tel(\\\"p\\\", textContent)\\n\\t);\\n}\\n\"}],\"toolbar\":false}"));</script><p>It is nice to use similar naming convention as native DOM API.</p><script src="https://cdn.jsdelivr.net/npm/shiki"></script><script src="index.js" type="module"></script></body></html> |