fix: handle dynamically loaded content

This commit is contained in:
2025-06-16 18:40:41 +03:00
parent 30c21cb8a3
commit ec31bb6385

View File

@@ -3,49 +3,75 @@ function escapeRegex(s) {
} }
function highlightWords(lists) { function highlightWords(lists) {
const textNodes = []; const processNodes = () => {
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { const textNodes = [];
acceptNode: node => { const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
if (node.parentNode && ['SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME'].includes(node.parentNode.nodeName)) return NodeFilter.FILTER_REJECT; acceptNode: node => {
if (!node.nodeValue.trim()) return NodeFilter.FILTER_SKIP; if (node.parentNode && node.parentNode.nodeName === 'MARK') return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT; if (node.parentNode && ['SCRIPT', 'STYLE', 'NOSCRIPT', 'IFRAME'].includes(node.parentNode.nodeName)) return NodeFilter.FILTER_REJECT;
} if (!node.nodeValue.trim()) return NodeFilter.FILTER_SKIP;
}); return NodeFilter.FILTER_ACCEPT;
}
while (walker.nextNode()) textNodes.push(walker.currentNode);
const activeWords = [];
for (const list of lists) {
if (!list.active) continue;
for (const word of list.words) {
if (!word.active) continue;
activeWords.push({
text: word.wordStr,
background: word.background || list.background,
foreground: word.foreground || list.foreground
});
}
}
if (activeWords.length === 0) return;
const wordMap = new Map();
for (const word of activeWords) wordMap.set(word.text.toLowerCase(), word);
const pattern = new RegExp(`(${Array.from(wordMap.keys()).map(escapeRegex).join('|')})`, 'gi');
for (const node of textNodes) {
if (!pattern.test(node.nodeValue)) continue;
const span = document.createElement('span');
span.innerHTML = node.nodeValue.replace(pattern, match => {
const word = wordMap.get(match.toLowerCase()) || { background: '#ffff00', foreground: '#000000' };
return `<mark style="background:${word.background};color:${word.foreground};padding:0 2px;">${match}</mark>`;
}); });
node.parentNode.replaceChild(span, node); while (walker.nextNode()) textNodes.push(walker.currentNode);
}
const activeWords = [];
for (const list of lists) {
if (!list.active) continue;
for (const word of list.words) {
if (!word.active) continue;
activeWords.push({
text: word.wordStr,
background: word.background || list.background,
foreground: word.foreground || list.foreground
});
}
}
if (activeWords.length === 0) return;
const wordMap = new Map();
for (const word of activeWords) wordMap.set(word.text.toLowerCase(), word);
const pattern = new RegExp(`(${Array.from(wordMap.keys()).map(escapeRegex).join('|')})`, 'gi');
for (const node of textNodes) {
if (!pattern.test(node.nodeValue)) continue;
const span = document.createElement('span');
span.innerHTML = node.nodeValue.replace(pattern, match => {
const word = wordMap.get(match.toLowerCase()) || { background: '#ffff00', foreground: '#000000' };
return `<mark style="background:${word.background};color:${word.foreground};padding:0 2px;">${match}</mark>`;
});
node.parentNode.replaceChild(span, node);
}
};
const debouncedProcessNodes = debounce(processNodes, 300);
debouncedProcessNodes();
const observer = new MutationObserver(debouncedProcessNodes);
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
window.addEventListener('scroll', debouncedProcessNodes);
}
// Debounce helper function
function debounce(func, wait) {
let timeout;
return function () {
const context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
} }
chrome.storage.local.get("lists", ({ lists }) => { chrome.storage.local.get("lists", ({ lists }) => {