Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active May 30, 2023 06:09
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save WebReflection/d3aad260ac5007344a0731e797c8b1a4 to your computer and use it in GitHub Desktop.
Save WebReflection/d3aad260ac5007344a0731e797c8b1a4 to your computer and use it in GitHub Desktop.
hyperHTML, the nitty gritty
// used to retrieve template content
const templates = new WeakMap;
// used to retrieve node updates
const updates = new WeakMap;
// hyperHTML, the nitty gritty
function hyperHTML(chunks, ...interpolations) {
// if the static chunks are unknown
// (once per application)
if (!templates.has(chunks)) {
// use modern HTMLTemplateElement ...
const template = document.createElement('template');
// ... to reliably inject content joined by a unique comment
template.innerHTML = chunks.join('<!--⚡️-->');
// associate unique chunks to ...
templates.set(chunks, {
// ... the DocumentFragment within the template ...
content: template.content,
// ... and a list of paths to retrieve nodes to update
paths: [].filter.call(
template.content.childNodes,
// consider only comments with the unique content
node => node.nodeType === Node.COMMENT_NODE &&
node.textContent === '⚡️'
).map(node => {
// a comment is no layout, but it's handy to have it around
// what we want though, is a text node to show our content
node = node.parentNode.insertBefore(
node.ownerDocument.createTextNode(''),
node
);
// we can now create a path to find the node
// which is used once on first template setup
// [0, 1] for <p>Hello <!--⚡️--></p>
const path = [];
do {
path.unshift(path.indexOf.call(node.parentNode.childNodes, node));
node = node.parentNode;
} while(node !== template.content);
return path;
})
});
}
// if the current context has no updates available ...
// (once per node that uses this specific template)
if (!updates.has(this) || updates.get(this).chunks !== chunks) {
// ... grab related info
const info = templates.get(chunks);
// ... insert a deep clone of the template content ...
this.replaceChildren(info.content.cloneNode(true));
// ... and save details for the next update
updates.set(this, {
// used to check the node is rendering the expected template
chunks,
// find all nodes that need to be updated
// save it as callback to invoke per each update
fns: info.paths.map(path => {
const node = path.reduce((p, i) => p.childNodes[i], this);
return text => node.textContent = text;
})
});
}
// we are now sure the template is known
// the content of the context is the epxected one
// and there is a list of updates to invoke
updates.get(this).fns.forEach((fn, i) => fn(interpolations[i]));
return this;
}
@WebReflection
Copy link
Author

hyperHTML and its family

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment