all repos — h3 @ bc817d0c08a19a5caf1a12a07b83744108a43249

A tiny, extremely minimalist JavaScript microframework.

Optimized children redrawing, resetting $key on redraw.
h3rald h3rald@h3rald.com
Fri, 01 May 2020 14:42:19 +0200
commit

bc817d0c08a19a5caf1a12a07b83744108a43249

parent

94248bd20d67a2d0c36b58e33996c2ee42319186

5 files changed, 124 insertions(+), 95 deletions(-)

jump to
M __tests__/vnode.js__tests__/vnode.js

@@ -129,15 +129,21 @@ expect(span).toEqual(node.childNodes[1]);

}); it("should provide a redraw method that is able to remove existing DOM nodes", () => { - const newvnode = h3("div", [h3("span")]); - const oldvnode = h3("div", [h3("span#a"), h3("span")]); - const node = oldvnode.render(); - const span = node.childNodes[1]; + let oldvnode = h3("div", [h3("span#a"), h3("span")]); + let newvnode = h3("div", [h3("span")]); + let node = oldvnode.render(); oldvnode.redraw({ node: node, vnode: newvnode }); expect(oldvnode).toEqual(newvnode); expect(oldvnode.children.length).toEqual(1); expect(node.childNodes.length).toEqual(1); - expect(span).toEqual(node.childNodes[0]); + oldvnode = h3("div.test-children", [h3("span.a"), h3("span.b")]); + node = oldvnode.render(); + newvnode = h3("div.test-children", [h3("div.c")]); + oldvnode.redraw({ node: node, vnode: newvnode }); + expect(oldvnode).toEqual(newvnode); + expect(oldvnode.children.length).toEqual(1); + expect(node.childNodes.length).toEqual(1); + expect(oldvnode.children[0].classList[0]).toEqual("c"); }); it("should provide a redraw method that is able to figure out differences in children", () => {
M docs/H3_DeveloperGuide.htmdocs/H3_DeveloperGuide.htm

@@ -7999,7 +7999,7 @@ </ul>

</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco &ndash; April 26, 2020</p> + <p><span class="copy"></span> Fabio Cevasco &ndash; May 1, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
M docs/example/assets/js/h3.jsdocs/example/assets/js/h3.js

@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);

oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";

@@ -398,14 +400,15 @@ });

oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k])) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;

@@ -418,40 +421,43 @@ }

} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1) { - if (oldvnode.children[i].type === "#text") { - oldvnode.children[i] = newvnode.children[i]; - node.childNodes[i].nodeValue = newvnode.children[i].value; - } else { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); - } + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); } } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),

@@ -462,11 +468,11 @@ notFoundInOld,

0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML
M docs/js/h3.jsdocs/js/h3.js

@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);

oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";

@@ -398,14 +400,15 @@ });

oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k])) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;

@@ -418,40 +421,43 @@ }

} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1) { - if (oldvnode.children[i].type === "#text") { - oldvnode.children[i] = newvnode.children[i]; - node.childNodes[i].nodeValue = newvnode.children[i].value; - } else { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); - } + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); } } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),

@@ -462,11 +468,11 @@ notFoundInOld,

0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML
M h3.jsh3.js

@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);

oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";

@@ -398,14 +400,15 @@ });

oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k]) && !map.includes(k)) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;

@@ -418,11 +421,13 @@ }

} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (newmap.length === oldmap.length && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { if (newmap[i] === -1 || oldmap[i] === -1) {

@@ -433,20 +438,26 @@ });

} } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),

@@ -457,11 +468,11 @@ notFoundInOld,

0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML