Skip to content

Commit

Permalink
perf: go back to calculating outerHTML morphed nodes manually. This i…
Browse files Browse the repository at this point in the history
…s 2n fewer array allocations, where n is the number of DOM nodes with children.
  • Loading branch information
botandrose-machine committed Jan 15, 2025
1 parent f522f06 commit 7d182ba
Showing 1 changed file with 26 additions and 15 deletions.
41 changes: 26 additions & 15 deletions src/idiomorph.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,30 +155,44 @@ var Idiomorph = (function () {
const newNode = normalizeParent(newContent);
const ctx = createMorphContext(oldNode, newNode, config);

return saveAndRestoreFocus(ctx, () => {
const morphedNodes = saveAndRestoreFocus(ctx, () => {
return withHeadBlocking(
ctx,
oldNode,
newNode,
/** @param {MorphContext} ctx */ (ctx) => {
let morphedNodes;
if (ctx.morphStyle === "innerHTML") {
morphedNodes = morphChildren(ctx, oldNode, newNode);
morphChildren(ctx, oldNode, newNode);
return Array.from(oldNode.childNodes);
} else {
// outerHTML
morphedNodes = morphChildren(
const oldParent = normalizeParent(oldNode);

// basis for calulating which nodes were morphed
// since there maybe unmorphed sibling nodes
let childNodes = Array.from(oldParent.childNodes);
const index = childNodes.indexOf(oldNode);
// how many elements are to the right of the oldNode
const rightMargin = childNodes.length - (index + 1);

morphChildren(
ctx,
normalizeParent(oldNode),
oldParent,
newNode,
oldNode,
oldNode.nextSibling,
);

// rebuild childNodes
childNodes = Array.from(oldParent.childNodes);
return childNodes.slice(index, childNodes.length - rightMargin);
}
ctx.pantry.remove();
return morphedNodes;
},
);
});

ctx.pantry.remove();
return morphedNodes;
}

/**
Expand Down Expand Up @@ -241,7 +255,6 @@ var Idiomorph = (function () {
* @param {Element} newParent the parent element of the new content
* @param {Node|null} [insertionPoint] the point in the DOM we start morphing at (defaults to first child)
* @param {Node|null} [endPoint] the point in the DOM we stop morphing at (defaults to after last child)
* @returns {Node[]}
*/
function morphChildren(
ctx,
Expand All @@ -263,7 +276,7 @@ var Idiomorph = (function () {
insertionPoint ||= oldParent.firstChild;

// run through all the new content
const morphedNodes = [...newParent.childNodes].map((newChild) => {
for (const newChild of newParent.childNodes) {
// once we reach the end of the old parent content skip to the end and insert the rest
if (insertionPoint && insertionPoint != endPoint) {
const bestMatch = findBestMatch(
Expand All @@ -279,7 +292,7 @@ var Idiomorph = (function () {
}
morphNode(bestMatch, newChild, ctx);
insertionPoint = bestMatch.nextSibling;
return bestMatch;
continue;
}
}

Expand All @@ -293,21 +306,19 @@ var Idiomorph = (function () {
ctx,
);
morphNode(movedChild, newChild, ctx);
return movedChild;
continue;
}

// last resort: insert the new node from scratch
return createNode(oldParent, newChild, insertionPoint, ctx);
});
createNode(oldParent, newChild, insertionPoint, ctx);
}

// remove any remaining old nodes that didn't match up with new content
while (insertionPoint && insertionPoint != endPoint) {
const tempNode = insertionPoint;
insertionPoint = insertionPoint.nextSibling;
removeNode(tempNode, ctx);
}

return morphedNodes.filter((e) => e != null);
}

/**
Expand Down

0 comments on commit 7d182ba

Please sign in to comment.