diff --git a/src/idiomorph.js b/src/idiomorph.js index 3c7d1d3..f78733f 100644 --- a/src/idiomorph.js +++ b/src/idiomorph.js @@ -384,6 +384,10 @@ var Idiomorph = (function () { let softMatch = null; let nextSibling = node.nextSibling; let siblingSoftMatchCount = 0; + let displaceMatchCount = 0; + + // max id matches we are willing to displace in our search + const nodeMatchCount = ctx.idMap.get(node)?.size || 0; let cursor = startPoint; while (cursor && cursor != endPoint) { @@ -397,11 +401,24 @@ var Idiomorph = (function () { if (softMatch === null) { // the current soft match will hard match something else in the future, leave it if (!ctx.idMap.has(cursor)) { - // save this as the fallback if we get through the loop without finding a hard match - softMatch = cursor; + // optimization: if node can't id set match, we can just return the soft match immediately + if (!nodeMatchCount) { + return cursor; + } else { + // save this as the fallback if we get through the loop without finding a hard match + softMatch = cursor; + } } } } + // check for ids we may be displaced when matching + displaceMatchCount += ctx.idMap.get(cursor)?.size || 0; + if (displaceMatchCount > nodeMatchCount) { + // if we are going to displace more ids than the node contains then + // we do not have a good candidate for an id match, so return + break; + } + if ( softMatch === null && nextSibling && diff --git a/test/ops.js b/test/ops.js index 1a1422d..e5fc4ce 100644 --- a/test/ops.js +++ b/test/ops.js @@ -188,4 +188,39 @@ describe("morphing operations", function () { ], ); }); + + it("findBestMatch rejects morphing node that would lose more IDs", function () { + // here the findBestMatch function when it finds a node with id's it will track how many + // id matches in this node and then as it searches for a matching node it will track + // how many id's in the content it would have to remove before it finds a match + // if it finds more ids are going to match in-between nodes it aborts matching to + // allow better matching with less dom updates. + assertOps( + `
` + + `` + + `` + + `` + + `
`, + + `
` + + `` + + `` + + `` + + `
`, + [ + [ + "Morphed", + '
', + '
', + ], + ["Morphed", "", ""], + ["Morphed", '', ''], + ["Added", ""], + ["Morphed", '', ''], + ["Morphed", "", ""], + ["Morphed", '', ''], + ["Removed", ""], + ], + ); + }); }); diff --git a/test/preserve-focus.js b/test/preserve-focus.js index 4381f24..17394b7 100644 --- a/test/preserve-focus.js +++ b/test/preserve-focus.js @@ -232,13 +232,7 @@ describe("Preserves focus where possible", function () { "b", false, ); - if (hasMoveBefore()) { - assertFocus("focused"); - // TODO moveBefore loses selection on Chrome 131.0.6778.264 - // expect will be fixed in future release - // assertFocusAndSelection("focused", "b"); - } else { - assertNoFocus(); - } + + assertFocus("focused"); }); });