add comments and tests for 219
This commit is contained in:
parent
91b294e648
commit
dbac7f8468
2 changed files with 25 additions and 1 deletions
20
mithril.js
20
mithril.js
|
|
@ -31,6 +31,16 @@ Mithril = m = new function app(window, undefined) {
|
||||||
}
|
}
|
||||||
function build(parentElement, parentTag, parentCache, parentIndex, data, cached, shouldReattach, index, editable, namespace, configs) {
|
function build(parentElement, parentTag, parentCache, parentIndex, data, cached, shouldReattach, index, editable, namespace, configs) {
|
||||||
//`build` is a recursive function that manages creation/diffing/removal of DOM elements based on comparison between `data` and `cached`
|
//`build` is a recursive function that manages creation/diffing/removal of DOM elements based on comparison between `data` and `cached`
|
||||||
|
//the diff algorithm can be summarized as this:
|
||||||
|
//1 - compare `data` and `cached`
|
||||||
|
//2 - if they are different, copy `data` to `cached` and update the DOM based on what the difference is
|
||||||
|
//3 - recursively apply this algorithm for every array and for the children of every virtual element
|
||||||
|
|
||||||
|
//the `cached` data structure is essentially the same as the previous redraw's `data` data structure, with a few additions:
|
||||||
|
//- `cached` always has a property called `nodes`, which is a list of DOM elements that correspond to the data represented by the respective virtual element
|
||||||
|
//- in order to support attaching `nodes` as a property of `cached`, `cached` is *always* a non-primitive object, i.e. if the data was a string, then cached is a String instance. If data was `null` or `undefined`, cached is `new String("")`
|
||||||
|
//- `cached also has a `configContext` property, which is the state storage object exposed by config(element, isInitialized, context)
|
||||||
|
//- when `cached` is an Object, it represents a virtual element; when it's an Array, it represents a list of elements; when it's a String, Number or Boolean, it represents a text node
|
||||||
|
|
||||||
//`parentElement` is a DOM element used for W3C DOM API calls
|
//`parentElement` is a DOM element used for W3C DOM API calls
|
||||||
//`parentTag` is only used for handling a corner case for textarea values
|
//`parentTag` is only used for handling a corner case for textarea values
|
||||||
|
|
@ -66,7 +76,7 @@ Mithril = m = new function app(window, undefined) {
|
||||||
data = flatten(data)
|
data = flatten(data)
|
||||||
var nodes = [], intact = cached.length === data.length, subArrayCount = 0
|
var nodes = [], intact = cached.length === data.length, subArrayCount = 0
|
||||||
|
|
||||||
//key algorithm: sort elements without recreating them if keys are present
|
//keys algorithm: sort elements without recreating them if keys are present
|
||||||
//1) create a map of all existing keys, and mark all for deletion
|
//1) create a map of all existing keys, and mark all for deletion
|
||||||
//2) add new keys to map and mark them for addition
|
//2) add new keys to map and mark them for addition
|
||||||
//3) if key exists in new list, change action from deletion to a move
|
//3) if key exists in new list, change action from deletion to a move
|
||||||
|
|
@ -126,6 +136,7 @@ Mithril = m = new function app(window, undefined) {
|
||||||
//end key algorithm
|
//end key algorithm
|
||||||
|
|
||||||
for (var i = 0, cacheCount = 0; i < data.length; i++) {
|
for (var i = 0, cacheCount = 0; i < data.length; i++) {
|
||||||
|
//diff each item in the array
|
||||||
var item = build(parentElement, parentTag, cached, index, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace, configs)
|
var item = build(parentElement, parentTag, cached, index, data[i], cached[cacheCount], shouldReattach, index + subArrayCount || subArrayCount, editable, namespace, configs)
|
||||||
if (item === undefined) continue
|
if (item === undefined) continue
|
||||||
if (!item.nodes.intact) intact = false
|
if (!item.nodes.intact) intact = false
|
||||||
|
|
@ -134,12 +145,18 @@ Mithril = m = new function app(window, undefined) {
|
||||||
cached[cacheCount++] = item
|
cached[cacheCount++] = item
|
||||||
}
|
}
|
||||||
if (!intact) {
|
if (!intact) {
|
||||||
|
//diff the array itself
|
||||||
|
|
||||||
|
//update the list of DOM nodes by collecting the nodes from each item
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
if (cached[i] != null) nodes = nodes.concat(cached[i].nodes)
|
if (cached[i] != null) nodes = nodes.concat(cached[i].nodes)
|
||||||
}
|
}
|
||||||
|
//remove items from the end of the array if the new array is shorter than the old one
|
||||||
|
//if errors ever happen here, the issue is most likely a bug in the construction of the `cached` data structure somewhere earlier in the program
|
||||||
for (var i = 0, node; node = cached.nodes[i]; i++) {
|
for (var i = 0, node; node = cached.nodes[i]; i++) {
|
||||||
if (node.parentNode != null && nodes.indexOf(node) < 0) clear([node], [cached[i]])
|
if (node.parentNode != null && nodes.indexOf(node) < 0) clear([node], [cached[i]])
|
||||||
}
|
}
|
||||||
|
//add items to the end if the new array is longer than the old one
|
||||||
for (var i = cached.nodes.length, node; node = nodes[i]; i++) {
|
for (var i = cached.nodes.length, node; node = nodes[i]; i++) {
|
||||||
if (node.parentNode == null) parentElement.appendChild(node)
|
if (node.parentNode == null) parentElement.appendChild(node)
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +222,7 @@ Mithril = m = new function app(window, undefined) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//corner case: replacing the nodeValue of a text node that is a child of a textarea/contenteditable doesn't work
|
//corner case: replacing the nodeValue of a text node that is a child of a textarea/contenteditable doesn't work
|
||||||
|
//we need to update the value property of the parent textarea or the innerHTML of the contenteditable element instead
|
||||||
if (parentTag === "textarea") parentElement.value = data
|
if (parentTag === "textarea") parentElement.value = data
|
||||||
else if (editable) editable.innerHTML = data
|
else if (editable) editable.innerHTML = data
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -744,6 +744,12 @@ function testMithril(mock) {
|
||||||
|
|
||||||
return unloaded1 === true && unloaded2 === true
|
return unloaded1 === true && unloaded2 === true
|
||||||
})
|
})
|
||||||
|
test(function() {
|
||||||
|
var root = mock.document.createElement("div")
|
||||||
|
m.render(root, [m("div.blue")])
|
||||||
|
m.render(root, [m("div.green", [m("div")]), m("div.blue")])
|
||||||
|
return root.childNodes.length == 2
|
||||||
|
})
|
||||||
//end m.render
|
//end m.render
|
||||||
|
|
||||||
//m.redraw
|
//m.redraw
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue