`deka-dom-el` — Introduction/Guide

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";
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"
		})
	)
);

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.

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:

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
);

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")
);
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")
		)
	)
);

Creating advanced (reactive) templates and components

Basic 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; }",
		".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.