Find a file
DarkWiiPlayer 0e6eee28fd Make DOMState API more generic
The old interface was too specific to the way domLense works, by
assuming the value to be an array that gets mutated elsewhere (or a
proxy that behaves like one)
2024-01-15 11:48:52 +01:00
domLense.js Refactor exports 2024-01-11 15:44:11 +01:00
license.md Update metadata 2023-10-04 10:35:56 +02:00
package.json Fix error when passing undefined into tag generator 2023-12-30 13:17:36 +01:00
readme.md Move description above import block in readme 2023-12-23 18:25:05 +01:00
skooma.js Refactor exports 2024-01-11 15:44:11 +01:00
state.js Make DOMState API more generic 2024-01-15 11:48:52 +01:00

Skooma

A functional-friendly helper library for procedural DOM generation and templating.

import {html} from "skooma.js"

Overview

const text = new State({value: "Skooma is cool"})
setTimeout(() => {text.value = "Skooma is awesome!"}, 1e5)

document.body.append(html.div(
    html.h1("Hello, World!"),
    html.p(text, {class: "amazing"}),
    html.button("Show Proof", {click: event => { alert("It's true!") }})
))

Interface / Examples

Basic DOM generation

Accessing the html proxy with any string key returns a new node generator function:

html.div("Hello, World!")

Attributes can be set by passing objects to the generator:

html.div("Big Text", {style: "font-size: 1.4em"})

Complex structures can easily achieved by nesting generator functions:

html.div(
    html.p(
        html.b("Bold Text")
    )
)

For convenience, arrays assigned as attributes will be joined with spaces:

html.a({class: ["button", "important"]})

Assigning a function as an attribute will instead attach it as an event listener:

html.button("Click me!", {click: event => {
    alert("You clicked the button.")
}})

Generators can be called with many arguments. Arrays get iterated recursively as if they were part of a flat argument list.

Generating Text Nodes

text("Hello, World")
// Wraps document.createTextNode
text()
// Defaults to empty string instead of erroring
text(null)
// Non-string arguments still error

text`Hello, World!`
// returns a new document fragment containing the text node "Hello, World!"
text`Hello, ${user}!`
// returns a document fragment containing 3 nodes:
// "Hello, ", the interpolated value of `user` and "!"
text`Hello, ${html.b(user)}!`
// Text node for Hello, the <b> tag with the user's name, and a text node for !

handle

import {handle} from 'skooma.js'

Since it is common for event handlers to call preventDefault(), skooma provides a helper function called handle with the following definition:

fn => event => { event.preventDefault(); return fn(event) }

A few more examples:

Create a Button that deletes itself:

document.body.append(
	html.button("Delete Me", {click: event => event.target.remove()})
)

Turn a two-dimensional array into an HTML table:

const table = rows =>
	html.table(html.tbody(rows.map(
		row => html.tr(row.map(
			cell => html.rd(cell, {dataset: {
				content: cell.toLowerCase(),
			}})
		))
	)))

A list that you can add items to

let list, input = ""
document.body.append(html.div([
	list=html.ul(),
	html.input({type: 'text', input: e => input = e.target.value}),
	html.button({click: event => list.append(html.li(input))}, "Add"),
]))

A list that you can also delete items from

const listItem = content => html.li(
	html.span(content), " ", html.a("[remove]", {
		click: event => event.target.closest("li").remove(),
		style: { cursor: 'pointer', color: 'red' },
	})
)
let list, input = ""
document.body.append(html.div([
	list=html.ul(),
	html.input({type: 'text', input: e => input = e.target.value}),
	html.button({click: event => list.append(listItem(input))}, "Add"),
]))