feat: fix find-in-page highlighting to handle multiple matches per text node and prevent DOM corruption
This commit is contained in:
parent
7ccad9c589
commit
b1cee1a0ec
@ -5020,27 +5020,45 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
highlightMatches() {
|
highlightMatches() {
|
||||||
this.matches.forEach((match, idx) => {
|
const matchesByNode = new Map();
|
||||||
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);
|
|
||||||
|
|
||||||
|
this.matches.forEach((match, idx) => {
|
||||||
|
if (!matchesByNode.has(match.node)) {
|
||||||
|
matchesByNode.set(match.node, []);
|
||||||
|
}
|
||||||
|
matchesByNode.get(match.node).push({ match, idx });
|
||||||
|
});
|
||||||
|
|
||||||
|
matchesByNode.forEach((entries, node) => {
|
||||||
|
if (!node || !node.parentNode) return;
|
||||||
|
|
||||||
|
const text = node.textContent || '';
|
||||||
|
let cursor = 0;
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
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');
|
const mark = document.createElement('mark');
|
||||||
mark.className = idx === this.currentIndex ? 'find-highlight find-highlight-active' : 'find-highlight';
|
mark.className = idx === this.currentIndex ? 'find-highlight find-highlight-active' : 'find-highlight';
|
||||||
mark.textContent = matchText;
|
mark.textContent = matchText;
|
||||||
mark.setAttribute('data-find-index', idx);
|
mark.setAttribute('data-find-index', idx);
|
||||||
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
if (before) fragment.appendChild(document.createTextNode(before));
|
|
||||||
fragment.appendChild(mark);
|
fragment.appendChild(mark);
|
||||||
if (after) fragment.appendChild(document.createTextNode(after));
|
|
||||||
|
match.element = mark;
|
||||||
|
cursor = match.index + match.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cursor < text.length) {
|
||||||
|
fragment.appendChild(document.createTextNode(text.substring(cursor)));
|
||||||
|
}
|
||||||
|
|
||||||
node.parentNode.replaceChild(fragment, node);
|
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');
|
const marks = contentArea.querySelectorAll('mark.find-highlight');
|
||||||
marks.forEach(mark => {
|
marks.forEach(mark => {
|
||||||
|
if (!mark.parentNode) return;
|
||||||
const text = mark.textContent;
|
const text = mark.textContent;
|
||||||
const textNode = document.createTextNode(text);
|
const textNode = document.createTextNode(text);
|
||||||
mark.parentNode.replaceChild(textNode, mark);
|
mark.parentNode.replaceChild(textNode, mark);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user