Add Use helper

This should finally solve the question of how to pull behaviour defined
in JS modules into static HTML without bending over backwards.
This commit is contained in:
Talia 2021-11-02 18:20:43 +01:00
parent f209112f58
commit 797b6448c8
2 changed files with 92 additions and 0 deletions

39
use.js Normal file
View file

@ -0,0 +1,39 @@
const apply = (func, node) => {
if (typeof func == "function") {
func(node)
} else if ("then" in func) {
func.then(result => apply(result, node))
} else if ("default" in func) {
func.default(node)
}
}
export const use = node => {
const code = Function("return (" + node.getAttribute("use") + ")")
const func = code()
apply(func, node)
}
export const observe = (root = document) => {
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.hasAttribute("use")) {
use(node)
}
})
})
})
observer.observe(root, {subtree: true, childList: true})
return observer
}
export const run = (root = document) => {
root.querySelectorAll("[use]").forEach(use)
}
export const install = () => {
observe(document)
run(document)
}
export default install

53
use.md Normal file
View file

@ -0,0 +1,53 @@
# Use
A helper module to connect the world of modular javascript with that of plain
HTML.
## Rationale
The problem this module tries to solve is that of connecting behaviours defined
in a JavaScript module to HTML tags without having to:
- Select elements by ID
- Build a custom system to select elements uniquely
- Generate the HTML nodes directly in JavaScript
The solution this module provides is a `use` attribute, which can be set on an
element in HTML, which will be evaluated as JS and interpreted as follows:
- If it is a function, call it on the element
- If it is a promise, await it and retry with its result
- If it is a module, retry with its `default` value
This allows setting specific functionality on individual elements.
**Note** the adjacency to custom elements and customized builtin elements, and
how patterns in the usage of `use` may often be refactored into either of these.
## Limitations
For performance reasons, this module does not listen on attribute changes.
Since the focus of this module is to interact with plain HTML, any nodes
modified within JavaScript fall outside of its scope, and any code that adds a
`use` attribute to them should just call the desired function directly on the
node.
## Interface / Examples
```js
use.run(root=document)
// Runs once on all the children of its root.
use.observe(root=document)
// Observes the given root and runs on all newly added
// child elements. Does not perform an initial run over
// already existing elements.
use.use(element)
// Runs once on a selected element.
// Normally not recommended.
use.install()
// Observes the entire document and performs
// an initial scan over existing elements.
```