Last active
November 14, 2018 03:32
-
-
Save asvny/f8d426b653d2201e276f5d448a975a51 to your computer and use it in GitHub Desktop.
hyperHTML Fork minimal (https://codepen.io/WebReflection/pen/JObreJ?editors=0010)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
// 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, '') | |
}`; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment