Skip to content

Instantly share code, notes, and snippets.

@bitsmanent
Last active March 21, 2022 16:36
Show Gist options
  • Save bitsmanent/e053d486c28e5101ddf6a1739a5151c7 to your computer and use it in GitHub Desktop.
Save bitsmanent/e053d486c28e5101ddf6a1739a5151c7 to your computer and use it in GitHub Desktop.
Render only the differences between nodes of a given target
function domapply(target, nodes) {
const children = target.childNodes;
var child, n, has;
nodes.forEach((node,i) => {
/* skip comment node */
if(node.nodeType == Node.COMMENT_NODE)
return;
child = children[i];
/* add missing nodes */
if(!child)
return target.appendChild(node.cloneNode(true));
/* replace different elements */
if(child.tagName != node.tagName) {
target.insertBefore(node.cloneNode(true), child);
target.removeChild(child);
return;
}
/* update text nodes */
if(child.nodeValue) {
if(child.nodeValue != node.nodeValue)
child.nodeValue = node.nodeValue;
return;
}
/* Sync attributes and properties. */
has = {};
Array.from(node.attributes).forEach(att => {
has[att.name] = 1;
/* add missing */
if(!child.getAttribute(att.name))
return child.setAttribute(att.name, att.value);
/* update */
if(child.getAttribute(att.name) != att.value)
child.setAttribute(att.name, att.value);
});
/* remove unmatching attributes */
Array.from(child.attributes).forEach(att => {
if(!has[att.name])
child.removeAttribute(att.name);
});
/* props */
has = {}
Object.keys(node).forEach(k => {
has[k] = 1;
if(child[k] != node[k])
child[k] = node[k];
});
Object.keys(child).forEach(k => {
if(!has[k])
delete child[k];
});
/* magic fields */
if(child.checked != node.checked)
child.checked = node.checked;
if(child.value != node.value)
child.value = node.value;
/* run on children */
domapply(child, node.childNodes);
});
/* remove exceeding nodes */
n = nodes.length;
while(children[n])
target.removeChild(children[n]);
}
@bitsmanent
Copy link
Author

bitsmanent commented Feb 4, 2022

Example

function render(target, template, data) {
	const div = document.createElement("div");

	try {
		div.innerHTML = template(data);
	} catch(e) {
		/* don't return parsing errors to the caller
		 * just show them in console */
		console.error(e);
	}
	domapply(target, div.childNodes);
}

const tpl = (d) => `Hello ${d.foo}`;
render(document.body, tpl, {foo:"bar"});

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