Comparing version 1.0.0 to 1.0.1
92
index.js
const assert = require('assert') | ||
const morph = require('./morph') | ||
@@ -7,61 +8,62 @@ module.exports = nanomorph | ||
// (obj, obj) -> obj | ||
// no parent | ||
// -> same: diff and walk children | ||
// -> not same: replace and return | ||
// old node doesn't exist | ||
// -> insert new node | ||
// new node doesn't exist | ||
// -> delete old node | ||
// nodes are not the same | ||
// -> diff nodes and apply patch to old node | ||
// nodes are the same | ||
// -> walk all child nodes and append to old node | ||
function nanomorph (newTree, oldTree) { | ||
assert.equal(typeof newTree, 'object', 'nanomorph: newTree should be an object') | ||
assert.equal(typeof oldTree, 'object', 'nanomorph: oldTree should be an object') | ||
const tree = walk(newTree, oldTree) | ||
return tree | ||
} | ||
// no parent | ||
// -> same: diff and recurse children | ||
// -> not same: replace and return | ||
// old node doesn't exist | ||
// -> insert new node | ||
// new node doesn't exist | ||
// -> delete old node | ||
// nodes are not the same | ||
// -> diff nodes and apply patch to old node | ||
// nodes are the same | ||
// -> tbd | ||
const tree = (function recurse (newNode, oldNode, index) { | ||
if (!oldNode) { | ||
// walk and morph a dom tree | ||
// (obj, obj) -> obj | ||
function walk (newNode, oldNode) { | ||
if (!oldNode) { | ||
return newNode | ||
} else if (!newNode) { | ||
return null | ||
} else if (newNode !== oldNode) { | ||
if (newNode.tagName !== oldNode.tagName) { | ||
return newNode | ||
} else if (!newNode) { | ||
return null | ||
} else if (!compare(newNode, oldNode)) { | ||
return diff(newNode, oldNode) | ||
} else { | ||
const length = childLength(newNode, oldNode) | ||
const newChildren = [] | ||
var i = 0 | ||
for (; i++; i < length) { | ||
const newEl = recurse(newNode.childNodes, oldNode.childNodes, i) | ||
if (newEl) newChildren.push(newEl) | ||
} | ||
oldNode.innerHTML = '' // will this maintain the DOM state alright? | ||
oldNode.appendChild(newChildren) | ||
morph(newNode, oldNode) | ||
updateChildren(newNode, oldNode) | ||
return oldNode | ||
} | ||
})(newTree, oldTree, 0) | ||
return tree | ||
} else { | ||
updateChildren(newNode, oldNode) | ||
return oldNode | ||
} | ||
} | ||
// compare if two nodes are equal | ||
// (obj, obj) -> bool | ||
function compare (newNode, oldNode) { | ||
return newNode === oldNode | ||
} | ||
// update the children of elements | ||
// (obj, obj) -> null | ||
function updateChildren (newNode, oldNode) { | ||
if (!newNode.childNodes || !oldNode.childNodes) return | ||
// compute the longest child length of two nodes | ||
// (obj, obj) -> num | ||
function childLength (newNode, oldNode) { | ||
const newLength = newNode.childNodes.length | ||
const oldLength = oldNode.childNodes.length | ||
return Math.max(oldLength, newLength) | ||
} | ||
const length = Math.max(oldLength, newLength) | ||
// diff elements and apply the resulting patch to the old node | ||
// todo (yw): copy over events | ||
// todo (yw): investigate what else to copy over | ||
// (obj, obj) -> null | ||
function diff (newNode, oldNode) { | ||
return newNode | ||
for (var i = 0; i < length; i++) { | ||
const newChildNode = newNode.childNodes[i] | ||
const oldChildNode = oldNode.childNodes[i] | ||
const retChildNode = walk(newChildNode, oldChildNode) | ||
if (!retChildNode) { | ||
if (oldChildNode) oldNode.removeChild(oldChildNode) | ||
} else if (!oldChildNode) { | ||
if (retChildNode) oldNode.appendChild(retChildNode) | ||
} else if (retChildNode !== oldChildNode) { | ||
oldNode.replaceChild(retChildNode, oldChildNode) | ||
} | ||
} | ||
} |
{ | ||
"name": "nanomorph", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "Hyper fast diffing algorithm for real DOM nodes", | ||
@@ -22,3 +22,5 @@ "main": "index.js", | ||
"license": "MIT", | ||
"dependencies": {}, | ||
"dependencies": { | ||
"xtend": "^4.0.1" | ||
}, | ||
"devDependencies": { | ||
@@ -25,0 +27,0 @@ "bel": "^4.4.3", |
@@ -13,16 +13,32 @@ # nanomorph [![stability][0]][1] | ||
var el1 = bel`<div>hello people</div>` | ||
var el2 = bel`<div>nanananana-na-no</div>` | ||
var el2 = bel`<div>teeny, tiny, tin bottle</div>` | ||
tree = nanomorph(bel`<div>hello people</div>`, tree) | ||
tree = nanomorph(bel`<div>hello people</div>`, tree) | ||
tree = nanomorph(bel`<div>nanananana-na-no</div>`, tree) | ||
tree = nanomorph(`<div>teeny, tiny, tin bottle</div>`, tree) | ||
``` | ||
update(el1) | ||
update(el2) | ||
update(el3) | ||
## Appending to the DOM | ||
```js | ||
const nanomorph = require('nanomorph') | ||
function update (el) { | ||
if (!tree) { | ||
tree = el | ||
document.body.appendChild(tree) | ||
} else { | ||
tree = nanomorph(el, tree) | ||
// create the initial tree, save it and append to DOM | ||
const tree = bel`<div>hello people</div>` | ||
const update = create(tree) | ||
document.body.appendChild(tree) | ||
// now each consecutive update will be rendered on the DOM | ||
update(nanomorph(bel`<div>hello people</div>`, tree)) | ||
update(nanomorph(bel`<div>nanananana-na-no</div>`, tree)) | ||
function create (el) { | ||
var tree = el | ||
return function update (el) { | ||
if (el === tree) { | ||
return tree | ||
} else { | ||
tree.parent.replaceChild(el, tree) | ||
tree = el | ||
return tree | ||
} | ||
} | ||
@@ -29,0 +45,0 @@ } |
75
test.js
@@ -45,2 +45,77 @@ const test = require('tape') | ||
}) | ||
t.test('nested', (t) => { | ||
t.test('should replace a node', (t) => { | ||
t.plan(1) | ||
const oldTree = html` | ||
<main><p>hello world</p></main> | ||
` | ||
const newTree = html` | ||
<main><div>hello world</div></main> | ||
` | ||
const res = nanomorph(newTree, oldTree) | ||
const expected = '<main><div>hello world</div></main>' | ||
t.equal(String(res), expected, 'result was expected') | ||
}) | ||
t.test('should replace a node', (t) => { | ||
t.plan(1) | ||
const oldTree = html` | ||
<main><p>hello world</p></main> | ||
` | ||
const newTree = html` | ||
<main><p>hello you</p></main> | ||
` | ||
const res = nanomorph(newTree, oldTree) | ||
const expected = '<main><p>hello you</p></main>' | ||
t.equal(String(res), expected, 'result was expected') | ||
}) | ||
t.test('should replace a node', (t) => { | ||
t.plan(1) | ||
const oldTree = html` | ||
<main><p>hello world</p></main> | ||
` | ||
const res = nanomorph(oldTree, oldTree) | ||
const expected = oldTree | ||
t.equal(res, expected, 'result was expected') | ||
}) | ||
t.test('should append a node', (t) => { | ||
t.plan(1) | ||
const oldTree = html` | ||
<main></main> | ||
` | ||
const newTree = html` | ||
<main><p>hello you</p></main> | ||
` | ||
const res = nanomorph(newTree, oldTree) | ||
const expected = '<main><p>hello you</p></main>' | ||
t.equal(String(res), expected, 'result was expected') | ||
}) | ||
t.test('should remove a node', (t) => { | ||
t.plan(1) | ||
const oldTree = html` | ||
<main><p>hello you</p></main> | ||
` | ||
const newTree = html` | ||
<main></main> | ||
` | ||
const res = nanomorph(newTree, oldTree) | ||
const expected = '<main></main>' | ||
t.equal(String(res), expected, 'result was expected') | ||
}) | ||
}) | ||
}) |
11852
8
206
96
1
+ Addedxtend@^4.0.1
+ Addedxtend@4.0.2(transitive)