Last active
July 1, 2016 18:21
-
-
Save yelouafi/12bb24ba3ec2d566319d569c2eba0054 to your computer and use it in GitHub Desktop.
This file contains 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Raw DOM Nostalgia</title> | |
<style> | |
.done { | |
text-decoration: line-through; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="rawc.js"></script> | |
<script> | |
function todo(title) { | |
return comp(` | |
<input type=checkbox @done="checked:click"> | |
<span class="{it.done ? 'done' : ''}">${title}</span> | |
`) | |
} | |
const c = comp(` | |
<input #input> | |
<button #add>Add</button> | |
<button #reverse>Reverse</button> | |
<ul each="todos"> | |
<li here></li> | |
</ul> | |
`, { | |
todos: [] | |
}) | |
c.add.onclick = () => { | |
c.todos.push(todo(c.input.value)) | |
c.input.value = '' | |
c.update() | |
} | |
c.reverse.onclick = () => { | |
c.todos = c.todos.reverse() | |
c.update() | |
} | |
document.body.appendChild(c.el) | |
</script> | |
</body> | |
</html> |
This file contains 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
function each(it, ac) { | |
for(let i = 0; i < it.length; i++) { | |
ac(it[i], i) | |
} | |
} | |
const expRE = /\{[\s\S]+\}/g | |
function interpolate(exp) { | |
if(exp.startsWith('{')) | |
return new Function(['it'], `return ${exp.substring(1, exp.length - 1)}`) | |
else { | |
const fns = [] | |
const body = 'return `' + exp.replace(expRE, s => { | |
fns.push(interpolate(s)) | |
return '${fns[' + (fns.length - 1) + '](it)}' | |
}) + '`' | |
const fn = new Function(['it', 'fns'], body) | |
return ctx => fn(ctx, fns) | |
} | |
} | |
function visitAttribute(attr, el, ctx, queue) { | |
const attrName = attr.name | |
const attrValue = attr.value | |
if(attrName.startsWith('#')) { | |
ctx[attrName.slice(1)] = el | |
} | |
else if(attrName.startsWith('@')) { | |
const id = attrName.slice(1) | |
const [prop, event] = attrValue.split(':') | |
Object.defineProperty(ctx, id, { get: () => el[prop] }) | |
if(event) { | |
el.addEventListener(event, () => ctx.update()) | |
} | |
} | |
else if(attrName === 'each') { | |
let tpl = el.children.length ? el.children[0] : null | |
if(tpl) | |
tpl.remove() | |
queue.push(() => { | |
const children = ctx[attrValue] | |
children.forEach(ch => { | |
if(tpl) { | |
if(!ch.wrapper) { | |
const node = tpl.cloneNode(true) | |
ch.wrapper = node.hasAttribute('here') ? node : node.querySelector('[here]') | |
ch.wrapper.appendChild(ch.el ? ch.el : ch) | |
} | |
el.appendChild(ch.wrapper) | |
} else | |
el.appendChild(ch.el ? ch.el : ch) | |
}) | |
if(el.children.length > children.length) { | |
for(let i = children.length; i < el.children.length; i++) { | |
el.children[i].remove() | |
} | |
} | |
}) | |
} | |
else if(expRE.test(attrValue)) { | |
const fn = interpolate(attrValue) | |
queue.push(() => attr.value = fn(ctx)) | |
} | |
} | |
function visitNode(node, ctx, queue) { | |
if(node.nodeType === 1) | |
travserse(node, ctx, queue) | |
else if(node.nodeType === 3 && expRE.test(node.nodeValue)) { | |
const fn = interpolate(node.nodeValue) | |
queue.push(() => node.nodeValue = fn(ctx)) | |
} | |
} | |
function travserse(el, ctx, queue) { | |
each(el.attributes, attr => { | |
visitAttribute(attr, el, ctx, queue) | |
}) | |
each(el.childNodes, node => { | |
visitNode(node, ctx, queue) | |
}) | |
} | |
function comp(html, ctx = {}) { | |
let tmp = document.createElement('div'); | |
tmp.innerHTML = html; | |
let el = tmp.children.length === 1 ? tmp.children[0] : tmp | |
ctx.el = el | |
const queue = [] | |
travserse(el, ctx, queue) | |
ctx.update = () => queue.forEach(ac => ac()) | |
ctx.update() | |
return ctx | |
} |
This file contains 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
"use strict" | |
function updateChildren(node, children) { | |
let actCh = node.firstElementChild | |
let curCh | |
for(let i = 0, len = children.length; i < len; i++) { | |
curCh = children[i] | |
if(!actCh) { | |
node.appendChild(curCh) | |
} else if(curCh !== actCh) { | |
const nextCh = actCh.nextElementSibling | |
if(curCh === nextCh) { | |
actCh.remove() | |
actCh = nextCh.nextElementSibling | |
} else { | |
node.insertBefore(curCh, actCh) | |
} | |
} else { | |
actCh = actCh.nextElementSibling | |
} | |
} | |
if(curCh) { | |
while(curCh.nextElementSibling) { | |
curCh.nextElementSibling.remove() | |
} | |
} | |
} | |
function domUpdater() { | |
const queue = [] | |
function bindAttr(node, attr, getValue) { | |
if(typeof attr === 'string') | |
attr = node.getAttributeNode(attr) | |
queue.push(() => { | |
const newval = getValue() | |
if(attr.value !== newval) { | |
attr.value = newval | |
} | |
}) | |
} | |
function bindText(node, getText) { | |
queue.push(() => { | |
const newtext = getText() | |
if(node.textContent !== newtext) { | |
node.textContent = newtext | |
} | |
}) | |
} | |
function bindChildren(node, getChildren) { | |
queue.push(() => { | |
updateChildren(node, getChildren()) | |
}) | |
} | |
function update(ac) { | |
if(ac) ac() | |
queue.forEach(task => task()) | |
} | |
return { | |
add : task => queue.push(task), | |
update, | |
bindAttr, | |
bindText, | |
bindChildren | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment