From 797b6448c8a81927b5356ea1ee58875adeed2bd5 Mon Sep 17 00:00:00 2001 From: DarkWiiPlayer Date: Tue, 2 Nov 2021 18:20:43 +0100 Subject: [PATCH] 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. --- use.js | 39 +++++++++++++++++++++++++++++++++++++++ use.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 use.js create mode 100644 use.md diff --git a/use.js b/use.js new file mode 100644 index 0000000..4397b0d --- /dev/null +++ b/use.js @@ -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 diff --git a/use.md b/use.md new file mode 100644 index 0000000..341d583 --- /dev/null +++ b/use.md @@ -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. +```