Skip to content

Instantly share code, notes, and snippets.

@FocusThen
Forked from themarcba/vdom-finished.html
Created June 26, 2020 08:00
Show Gist options
  • Save FocusThen/5ba921b77051eb9a136008793e821cc7 to your computer and use it in GitHub Desktop.
Save FocusThen/5ba921b77051eb9a136008793e821cc7 to your computer and use it in GitHub Desktop.
Vue.js-like Virtual DOM
<div id="app"></div>
<script>
// Create virtual node
function h(tag, props, children) {
// Return the virtual node
return {
tag,
props,
children,
}
}
// Mount a virtual node to the DOM
function mount(vnode, container) {
// Create the element
const el = (vnode.el = document.createElement(vnode.tag))
// Set properties
for (const key in vnode.props) {
el.setAttribute(key, vnode.props[key])
}
// Handle children
if (typeof vnode.children === 'string') {
el.textContent = vnode.children
} else {
vnode.children.forEach(child => {
mount(child, el)
})
}
// Mount to the DOM
container.appendChild(el)
}
// Unmount a virtual node from the DOM
function unmount(vnode) {
vnode.el.parentNode.removeChild(vnode.el)
}
// Take 2 virtual nodes, compare & figure out what's the difference
function patch(n1, n2) {
const el = (n2.el = n1.el)
// Case where the nodes are of different tags
if (n1.tag !== n2.tag) {
mount(n2, el.parentNode)
unmount(n1)
}
// Case where the nodes are of the same tag
else {
// New virtual node has string children
if (typeof n2.children === 'string') {
el.textContent = n2.children
}
// New virtual node has array children
else {
// Old virtual node has string children
if (typeof n1.children === 'string') {
el.textContent = ''
n2.children.forEach(child => mount(child, el))
}
// Case where the new vnode has string children
else {
const c1 = n1.children
const c2 = n2.children
const commonLength = Math.min(c1.length, c2.length)
// Patch the children both nodes have in common
for (let i = 0; i < commonLength; i++) {
patch(c1[i], c2[i])
}
// Old children was longer
// Remove the children that are not "there" anymore
if (c1.length > c2.length) {
c1.slice(c2.length).forEach(child => {
unmount(child)
})
}
// Old children was shorter
// Add the newly added children
else if (c2.length > c1.length) {
c2.slice(c1.length).forEach(child => {
mount(child, el)
})
}
}
}
}
}
// Create virtual nodes & render them below this line 👇
// Virtual node 1
const node1 = h('div', { class: 'container' }, [
h('h1', null, 'Hello World 🌍'),
h('p', null, 'Thanks for reading the marc.dev blog 😊'),
])
// Mount the node to the DOM
mount(node1, document.getElementById('app'))
// Virtual node 2
const node2 = h('div', { class: 'container' }, [
h('h1', null, 'Hello Dev 💻'),
h('p', null, [
h('span', null, 'Thanks for reading the '),
h('a', { href: 'https://marc.dev' }, 'marc.dev'),
h('span', null, ' blog'),
]),
h(
'img',
{
src: 'https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif',
style: 'width: 350px; border-radius: 0.5rem;',
},
[],
),
])
// Patch the new node to the old node
setTimeout(() => {
patch(node1, node2)
}, 3000)
</script>
<div id="app"></app>
<script>
// Create virtual node
function h(tag, props, children) {
// Return the virtual node
}
// Mount a virtual node to the DOM
function mount(vnode, container) {
// Create the element
// Set props
// Handle children
// Mount to the DOM
}
// Unmount a virtual node from the DOM
function unmount(vnode) {
// Unmount the virtual node
}
// Take 2 vnodes, compare & figure out what's the difference
function patch(n1, n2) {
// Case where the nodes are of different tags
// Case where the nodes are of the same tag
// Case where the new vnode has string children
// Case where the new vnode has an array of vnodes
}
// Create virtual nodes & render them below this line...
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment