Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve id matching during findBestMatch #94

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/idiomorph.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 &&
Expand Down
35 changes: 35 additions & 0 deletions test/ops.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
`<div>` +
`<label>1</label><input id="first">` +
`<label>2</label><input id="second">` +
`<label>3</label><input id="third">` +
`</div>`,

`<div>` +
`<label>3</label><input id="third">` +
`<label>1</label><input id="first">` +
`<label>2</label><input id="second">` +
`</div>`,
[
[
"Morphed",
'<div><label>1</label><input id="first"><label>2</label><input id="second"><label>3</label><input id="third"></div>',
'<div><label>3</label><input id="third"><label>1</label><input id="first"><label>2</label><input id="second"></div>',
],
["Morphed", "<label>1</label>", "<label>3</label>"],
["Morphed", '<input id="third">', '<input id="third">'],
["Added", "<label>1</label>"],
["Morphed", '<input id="first">', '<input id="first">'],
["Morphed", "<label>2</label>", "<label>2</label>"],
["Morphed", '<input id="second">', '<input id="second">'],
["Removed", "<label>3</label>"],
],
);
});
});
10 changes: 2 additions & 8 deletions test/preserve-focus.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});