Skip to content

Instantly share code, notes, and snippets.

@ryanmorr
Last active March 29, 2024 06:38
Show Gist options
  • Save ryanmorr/63a174e84c9afd1e90da7d36f7953cfa to your computer and use it in GitHub Desktop.
Save ryanmorr/63a174e84c9afd1e90da7d36f7953cfa to your computer and use it in GitHub Desktop.
A tiny virtual DOM implementation
function updateAttribute(element, name, newVal, oldVal = null) {
if (name[0] === 'o' && name[1] === 'n') {
name = name.slice(2).toLowerCase();
if (newVal) {
element.addEventListener(name, newVal);
}
if (oldVal) {
element.removeEventListener(name, oldVal);
}
} else if (newVal == null || newVal === false) {
element.removeAttribute(name);
} else {
element.setAttribute(name, newVal);
}
}
function createElement(vnode) {
if (typeof vnode === 'string') {
return new Text(vnode);
}
const element = document.createElement(vnode.tag), props = vnode.props;
Object.keys(props).forEach((name) => updateAttribute(element, name, props[name]));
vnode.children.map(createElement).forEach((node) => element.appendChild(node));
return element;
}
function patch(parent, newNode, oldNode = null, index = 0) {
const element = parent.childNodes[index];
if (oldNode == null) {
return parent.appendChild(createElement(newNode));
}
if (newNode == null) {
return parent.removeChild(element);
} else if (typeof newNode !== typeof oldNode
|| typeof newNode === 'string' && newNode !== oldNode
|| newNode.tag !== oldNode.tag) {
return parent.replaceChild(createElement(newNode), element);
}
if (newNode.tag) {
for (const name in Object.assign({}, newNode.props, oldNode.props)) {
updateAttribute(element, name, newNode.props[name], oldNode.props[name]);
}
for (let i = 0; i < Math.max(newNode.children.length, oldNode.children.length); ++i) {
patch(element, newNode.children[i], oldNode.children[i], i);
}
}
}
function h(tag, props, ...children) {
props = props || {};
return {tag, props, children};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment