feat: fix find-in-page highlighting to handle multiple matches per text node and prevent DOM corruption

This commit is contained in:
Bruno Charest 2026-03-24 21:38:30 -04:00
parent 7ccad9c589
commit b1cee1a0ec

View File

@ -5020,27 +5020,45 @@
},
highlightMatches() {
const matchesByNode = new Map();
this.matches.forEach((match, idx) => {
const node = match.node;
const text = node.textContent;
const before = text.substring(0, match.index);
const matchText = text.substring(match.index, match.index + match.length);
const after = text.substring(match.index + match.length);
if (!matchesByNode.has(match.node)) {
matchesByNode.set(match.node, []);
}
matchesByNode.get(match.node).push({ match, idx });
});
const mark = document.createElement('mark');
mark.className = idx === this.currentIndex ? 'find-highlight find-highlight-active' : 'find-highlight';
mark.textContent = matchText;
mark.setAttribute('data-find-index', idx);
matchesByNode.forEach((entries, node) => {
if (!node || !node.parentNode) return;
const text = node.textContent || '';
let cursor = 0;
const fragment = document.createDocumentFragment();
if (before) fragment.appendChild(document.createTextNode(before));
fragment.appendChild(mark);
if (after) fragment.appendChild(document.createTextNode(after));
entries.sort((a, b) => a.match.index - b.match.index);
entries.forEach(({ match, idx }) => {
if (match.index > cursor) {
fragment.appendChild(document.createTextNode(text.substring(cursor, match.index)));
}
const matchText = text.substring(match.index, match.index + match.length);
const mark = document.createElement('mark');
mark.className = idx === this.currentIndex ? 'find-highlight find-highlight-active' : 'find-highlight';
mark.textContent = matchText;
mark.setAttribute('data-find-index', idx);
fragment.appendChild(mark);
match.element = mark;
cursor = match.index + match.length;
});
if (cursor < text.length) {
fragment.appendChild(document.createTextNode(text.substring(cursor)));
}
node.parentNode.replaceChild(fragment, node);
// Update reference to the mark element
match.element = mark;
});
},
@ -5050,6 +5068,7 @@
const marks = contentArea.querySelectorAll('mark.find-highlight');
marks.forEach(mark => {
if (!mark.parentNode) return;
const text = mark.textContent;
const textNode = document.createTextNode(text);
mark.parentNode.replaceChild(textNode, mark);