Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
hyperHTML, the nitty gritty
// used to retrieve template content
const templates = new Map;
// 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);
// ... clean up the node
this.textContent = '';
// ... append a deep clone of the template content ...
this.appendChild(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

WebReflection commented Nov 7, 2017

Example - live on Code Pen

// example
// bind a node to hyperHTML
const render = hyperHTML.bind(document.body);

// update at speed light its content
(function showTime() {
  requestAnimationFrame(showTime);
  render`about the time ${
    (new Date).toISOString().replace(/^.+T|Z$/g, '')
  }`;
}());

@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