4.1 KiB
Skooma
A functional-friendly helper library for procedural DOM generation and templating.
Skooma provides two proxies: html
and svg
.
Interface / Examples
html.div()
// Creates a <div> element
html.div("hello", "world")
// Creates a <div> element with the content "helloworld"
html.div(html.span())
// Creates a <div> element with a nested <span> element
html.div([html.span(), html.span()])
// Creates a <div> element with two nested <span> elements
html.div({class: "foo"})
// Creates a <div> with the class "foo"
html.div({class: ["foo", "bar"]})
// Creates a <div> with the classes "foo" and "bar"
html.div({click: 1})
// Creates a <div> with the attribute "click" set to "1"
html.div({click: event => console.log(event.target)})
// Creates a <div> with an event listener for "click" events
html.div(player: {username: "KhajiitSlayer3564"})
// Creates a <div> with the attribute "player" set to a JSON-encoded Object
html.div(self => self.innerHTML = "Hello, World!")
// Creates a <div> and passes it to a function that sets its inner HTML
html.div({foo: true})
// Creates a <div>, adds the attribute "foo"
html.div({foo: "bar"}, {foo: false})
// Creates a <div>, sets the "foo" attribute to "bar", then removes it again
// Special keys:
html.div(dataset: {foo: 1, bar: 2}) // Creates a <div> with the attributes "data-foo" and "data-bar" set to 1 and 2 html.div(style: {color: 'red'}) // Creates a <div> with the "style" attribute set to "color: red"
Generators can be called with many arguments. Arrays get iterated recursively as if they were part of a flat argument list.
text("Hello, World")
// Wraps document.createTextNode
text()
// Defaults to empty string instead of erroring
text(null)
// Non-string arguments still error
bind
This function offers a generic mechanism for binding elements to dynamic state. It takes a register function that satisfies the following criteria:
- It returns an initial state as an array
- It accepts a callback function
- On state change, it calls it with the new state as its arguments
And returns a second function, which takes a transformation (another functuion) from input state to DOM node. This transformation will be used to create an initial element from the initial state, which will be returned.
On every state change, the transform ation will be called on the new state to generate a new DOM Node, which replace the current one.
If the current node is not in the DOM yet, nothing will happen, and the new node is thrown away. This "stale" node will not update until it has been placed in the DOM and the next state change occurs.
bind(register)(html.div)
// Returns a div element bound to register
// Assuming register is a higher order function
// and html.div is a transformation from input state to a <div> node
handle
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"),
]))