Precise DOM morphing
morphing typescript dom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve matching logic

+52 -44
+24 -23
dist/morphlex.js
··· 47 47 elem.lastChild?.remove(); 48 48 } 49 49 function morphChildNode(child, guide, idMap, parent) { 50 - if (isElement(child) && isElement(guide)) { 51 - let current = child; 52 - let nextMatchByTagName = null; 53 - while (current) { 54 - if (isElement(current)) { 55 - if (current.id !== "" && current.id === guide.id) { 56 - morphNodes(current, guide, idMap, child, parent); 57 - break; 50 + if (isElement(child) && isElement(guide)) 51 + morphChildElement(child, guide, idMap, parent); 52 + else 53 + morphNodes(child, guide, idMap); 54 + } 55 + function morphChildElement(child, guide, idMap, parent) { 56 + let current = child; 57 + let nextMatchByTagName = null; 58 + const b = idMap.get(guide); 59 + while (current) { 60 + if (isElement(current)) { 61 + if (current.id !== "" && current.id === guide.id) { 62 + return morphNodes(current, guide, idMap, child, parent); 63 + } 64 + else { 65 + const a = idMap.get(current); 66 + if (a && b && [...a].some((it) => b.has(it))) { 67 + return morphNodes(current, guide, idMap, child, parent); 58 68 } 59 - else { 60 - const a = idMap.get(current); 61 - const b = idMap.get(guide); 62 - if (a && b && [...a].some((it) => b.has(it))) { 63 - return morphNodes(current, guide, idMap, child, parent); 64 - } 65 - else if (!nextMatchByTagName && current.tagName === guide.tagName) { 66 - nextMatchByTagName = current; 67 - } 69 + else if (!nextMatchByTagName && current.tagName === guide.tagName) { 70 + nextMatchByTagName = current; 68 71 } 69 72 } 70 - current = current.nextSibling; 71 73 } 72 - if (nextMatchByTagName) 73 - morphNodes(nextMatchByTagName, guide, idMap, child, parent); 74 - else 75 - child.replaceWith(guide.cloneNode(true)); 74 + current = current.nextSibling; 76 75 } 76 + if (nextMatchByTagName) 77 + morphNodes(nextMatchByTagName, guide, idMap, child, parent); 77 78 else 78 - morphNodes(child, guide, idMap); 79 + child.replaceWith(guide.cloneNode(true)); 79 80 } 80 81 function populateIdMapForNode(node, idMap) { 81 82 const elementsWithIds = node.querySelectorAll("[id]");
+28 -21
src/morphlex.ts
··· 41 41 else if (childB) elem.appendChild(childB.cloneNode(true)); 42 42 } 43 43 44 + // This is separate because the loop above might modify the length of the element's child nodes. 44 45 while (elem.childNodes.length > guide.childNodes.length) elem.lastChild?.remove(); 45 46 } 46 47 47 48 function morphChildNode(child: ChildNode, guide: ChildNode, idMap: IdMap, parent: Element): void { 48 - if (isElement(child) && isElement(guide)) { 49 - let current: ChildNode | null = child; 50 - let nextMatchByTagName: ChildNode | null = null; 49 + if (isElement(child) && isElement(guide)) morphChildElement(child, guide, idMap, parent); 50 + else morphNodes(child, guide, idMap); 51 + } 52 + 53 + function morphChildElement(child: Element, guide: Element, idMap: IdMap, parent: Element): void { 54 + const guideIdSet = idMap.get(guide); 55 + 56 + let current: ChildNode | null = child; 57 + let nextMatchByTagName: ChildNode | null = null; 51 58 52 - while (current) { 53 - if (isElement(current)) { 54 - if (current.id !== "" && current.id === guide.id) { 55 - morphNodes(current, guide, idMap, child, parent); 56 - break; 57 - } else { 58 - const a = idMap.get(current); 59 - const b = idMap.get(guide); 59 + // Try find a match by idSet, while also looking out for the next best match by tagName. 60 + while (current) { 61 + if (isElement(current)) { 62 + if (current.id !== "" && current.id === guide.id) { 63 + // Exact match by id. 64 + return morphNodes(current, guide, idMap, child, parent); 65 + } else { 66 + const currentIdSet = idMap.get(current); 60 67 61 - if (a && b && [...a].some((it) => b.has(it))) { 62 - return morphNodes(current, guide, idMap, child, parent); 63 - } else if (!nextMatchByTagName && current.tagName === guide.tagName) { 64 - nextMatchByTagName = current; 65 - } 68 + if (currentIdSet && guideIdSet && [...currentIdSet].some((it) => guideIdSet.has(it))) { 69 + // Match by idSet. 70 + return morphNodes(current, guide, idMap, child, parent); 71 + } else if (!nextMatchByTagName && current.tagName === guide.tagName) { 72 + nextMatchByTagName = current; 66 73 } 67 74 } 68 - 69 - current = current.nextSibling; 70 75 } 71 76 72 - if (nextMatchByTagName) morphNodes(nextMatchByTagName, guide, idMap, child, parent); 73 - else child.replaceWith(guide.cloneNode(true)); 74 - } else morphNodes(child, guide, idMap); 77 + current = current.nextSibling; 78 + } 79 + 80 + if (nextMatchByTagName) morphNodes(nextMatchByTagName, guide, idMap, child, parent); 81 + else child.replaceWith(guide.cloneNode(true)); 75 82 } 76 83 77 84 function populateIdMapForNode(node: ParentNode, idMap: IdMap): void {