README
DOMK
Powerful DOM toolkit
Installation
npm install domk --save
Why domk ?
- Domk is made for small and medium projects.
- Domk is the best fit for SSR projects.
- No boilerplate, principle, configuration needed.
- With Domk you can manipulate DOM easier and more flexible.
- Domk is an alternative for jQuery or static template engines.
- Domk is very compact, only 3kb after GZiped
Getting started
Let's create simple clock app
import domk from "domk";
document.body.innerHTML = `<h1></h1>`;
// create date formatter
const formatter = new Intl.DateTimeFormat("en", {
hour12: true,
hour: "numeric",
minute: "2-digit",
second: "2-digit",
});
const Clock = domk.one(
// select h1 element, the first argument is query selector text
// if there are many h1 elements in the document,
// the first one will be selected because we call domk.one()
// using domk.all() to update multiple matched elements at once
"h1",
// define updates for selected elements
function () {
return {
// update element text
text: formatter.format(new Date()),
};
}
);
// call Clock.update every 1 second
setInterval(Clock.update, 1000);
We can make the above example more compact
setInterval(
domk.one("h1", () => ({ text: new Date().toString() })).update,
1000
);
Click counter app
import domk from "domk";
let count = 0;
let Counter;
document.body.innerHTML = `<button></button>`;
function handleClick() {
count++;
Counter.update(count);
}
Counter = domk.one(
"button",
// model is value of count
function (model) {
return {
text: model
? `Clicked ${model} ${model === 1 ? "time" : "times"}`
: "Click here",
// define event listeners
on: {
// click event
click: handleClick,
},
};
}
);
Counter.update(count);
List rendering
In this example, we create a list of youtube links, Domk does not generate HTML tags automatically (like React, Angular do), We must provide some existing tags, those will be used for templating
let cats = [
{ id: "J---aiyznGQ", name: "Keyboard Cat" },
{ id: "z_AbfPXTKms", name: "Maru" },
{ id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
];
// UL is list container
// The first child of UL will be used for templating
document.body.innerHTML = `
<h1>The Famous Cats of YouTube</h1>
<!-- UL is list container -->
<ul>
<li>
<a target="_blank" ></a>
</li>
</ul>
`;
// define Cat component, no update() call needed
// when the Cat component is created, it is just like a template
// it will be called later on by another component
const Cat = domk.one("a", (cat) => ({
href: `https://www.youtube.com/watch?v=${cat.id}`,
text: `${cat.index}: ${cat.name}`,
}));
domk
.one("ul", (cats) => ({
children: {
// define model for children
model: cats.map((cat, index) => ({ ...cat, index: index + 1 })),
// each child element will be updated by Cat component
update: Cat,
},
}))
.update(cats);
Benchmark
Advanced Usages
References
domk(options)
Create a new Domk component with specified options.
- options (optional): A plain object has following properties
- model (optional): A function returns current model or a model object
- container (optional): A dom node or query selector string. By default, domk uses document as container
- return: Domk component
const mutableTodos = [];
let immutableTodos = [];
domk({ model: mutableTodos });
domk({ model: () => immutableTodos });
domk({ container: "#app" });
domk({ container: document.body });
domk.one(selector, updateFn) & domk.all(selector, updateFn)
Query single (domk.one) or multiple elements (domk.all) and apply updating specs to matched elements.
- selector: A valid query selector string that can be used to querySelector() and querySelectorAll()
- updateFn: A function retrieves 2 arguments model and context and returns updating specs.
Domk component
An object contains all update specs for specified element
.one(selector, updateFn) & Domk.all(selector, updateFn)
Perform the same as domk.all and domk.one
.update()
Domk.update(model, container)
Domk.update(container)
model()
model(props)
model(props, reducer)
model(reducer)
domk.nested()
- domk.nested(modelFn)
domk.children()
domk.children(modelFn)
domk.children(modelFn, component)
domk.children(modelFn, updateFn)
domk.children(modelFn, keyFn, component)
domk.children(modelFn, keyFn, updateFn)
Updating specs
A plain object has following properties
id
Update element id attribute
domk.one("div", () => ({ id: "new-id" })).update();
<div id="new-id"></div>
name
Update element name attribute
class
Update element class. A value can be string or class map object.
let isVisible = false;
domk.one("div.box1", () => ({ class: { hide: !isVisible } })).update();
domk
.one("div.box2", () => ({ class: isVisible ? "visible" : "invisible" }))
.update();
<div class="box1 hide"></div>
<div class="box2 invisible"></div>
domk does not remove original classes (box1, box2), it just append updated classes (hide, visible, invisible)
style
Update element style. A value can be string or style map object.
domk.one("div.box1", () => ({ style: "font-weight: bold" })).update();
domk.one("div.box2", () => ({ style: { opacity: 0.5 } })).update();
<div class="box1" style="font-weight: bold"></div>
<div class="box2" style="opacity: 0.5"></div>
domk does not add browser prefixes automatically
selected
Update selected property of option element
checked
Update checked property of input element
disabled
Update disabled property of input element
value
Update value property of input element
href
Update href attribute of anchor element
text
Update textContent property of html element
domk.one("div", { text: "<strong>This is formatted text</strong>" }).update();
<div><strong>This is formatted text</strong></div>
html
Update innerHTML property of html element
domk.one("div", { html: "<strong>This is formatted text</strong>" }).update();
<div><strong>This is formatted text</strong></div>
init
An init value for a current node. Init value can be function, Node object or HTML string.
- A function retrieves current node object as first argument.
- HTML string: Node contents will be replaced with given value.
- Node object: Clone of given node will be appended to current node.
domk
.one("div.box1", { init: "<strong>This is formatted text</strong>" })
.update();
domk.one("div.box2", { init: document.querySelector("#content") }).update();
const Box3 = domk.one("div.box3", {
init(node) {
console.log(node.innerHTML); // Box 3 contents
},
});
// init action invoked once
Box3.update();
Box3.update();
Box3.update();
<div id="content"><button>Click me</button></div>
<div class="box1"><strong>This is formatted text</strong></div>
<div class="box2">
<div id="content"><button>Click me</button></div>
</div>
<div class="box3">Box 3 contents</div>
on
Update event listeners
domk
.one("div", {
on: {
click() {
alert("Hi there");
},
mouseover(e) {
console.log("You are hovering", e.target);
},
mouseout(e) {
console.log("You leave", e.target);
},
},
})
.update();
prop
Update multiple properties at once
domk.one("button", { prop: { disabled: true, value: "Click me" } }).update();
attr
Update multiple attributes at once
domk.one("a", {
attr: {
href: "http://google.com",
title: "Click me",
},
});
children
Update the child node of the current node according to the specified model.
domk
.one(".list1", {
children: {
model: [1, 2, 3],
update: (number) => ({ text: number }),
},
})
.update();
domk
.one(".list2", (letters) => ({
children: {
model: letters,
update: (letter) => ({ text: letter }),
},
}))
.update(["A", "B", "C"]);
Before updating
<ul class="list1">
<!-- LI element is used to templating -->
<li></li>
</ul>
<ul class="list2">
<!-- LI element is used to templating -->
<li></li>
</ul>
After updating
<ul class="list1">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<ul class="list2">
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
Dependencies
Nothing