我正在制作一个google chrome扩展,突出显示文档中特定单词的长列表。当你把鼠标悬停在那个单词上时,它会显示一个iframe。问题是,由于列表非常大(大约1500个单词,目标是达到10,000或更多),在所有元素中循环并试图将这些元素与任何单词匹配会导致google chrome的真正延迟。这是我所拥有的,并按预期工作,但它会导致浏览器冻结,直到它完成长循环。代码(注意,我看到的是索引,因为有时文本包含多个需要高亮显示的单词):
$("*").contents()。Each (function () {
if (this.nodeType == 3) { //if text
if ($(this).text().trim() != '') {
if ($(this).text().charAt(0).includes('.') || $(this).text().charAt(0).includes('{') || $(this).text().charAt(0).includes('@') || $(this).text().charAt(0).includes('!') || $(this).text().charAt(0).includes('(') || $(this).text().charAt(0).includes(' ') || $(this).text().charAt(0).includes(':') || $(this).text().charAt(0).includes('<') || $(this).text().includes('document.') || $(this).text().includes('html') || $(this).text().includes('var ') || $(this).text().charAt(0).includes(',') || $(this).text().charAt(0).includes('[') || $(this).text().charAt(0).includes('^') || $(this).text().charAt(0).includes('n')) {
return
} else {
let checkIfTitle = $(this).parent()[0] as HTMLElement;
if(!checkIfTitle?.outerHTML){
return;
}
if(checkIfTitle.outerHTML.includes('<title')){
return
} else {
checkGlobalWords = false;
wordsHighlight.forEach((word) => {
if ($(this).text().includes(word.word)){
checkGlobalWords = true;
console.log($(this).text());
} else {
return;
}
})
if(checkGlobalWords){
console.log($(this).text());
wordsHighlight.forEach((word, index) => {
let helperElement = $(this).parent()[0] as HTMLElement
if(helperElement.id === 'global-solution'){
return;
}
if(helperElement.tagName === 'TITLE'){
return;
}
if(helperElement.tagName === 'SCRIPT'){
return;
}
if(helperElement.tagName === 'STYLE'){
return;
}
if(index === 0){
textHelperMultipleWords = $(this).text();
if(textHelperMultipleWords.includes(word.word)){
textHelperMultipleWords = textHelperMultipleWords.replace(word.word, `<span id="global-solution" style="background-color: #ebf5f9; cursor: pointer; font-style: italic; text-transform: capitalize;">${word.word}</span>`)
}
return;
}
if(index >= 1 && index < (wordsHighlight.length-1)){
if(textHelperMultipleWords.includes(word.word)){
textHelperMultipleWords = textHelperMultipleWords.replace(word.word, `<span id="global-solution" style="background-color: #ebf5f9; cursor: pointer; font-style: italic; text-transform: capitalize;">${word.word}</span>`)
}
}
//console.log(index, wordsHighlight.length);
if(index === (wordsHighlight.length-1)){
if(textHelperMultipleWords.includes(word.word)){
textHelperMultipleWords = textHelperMultipleWords.replace(word.word, `<span id="global-solution" style="background-color: #ebf5f9; cursor: pointer; font-style: italic; text-transform: capitalize;">${word.word}</span>`)
}
if(textHelperMultipleWords !== $(this).text()){
$(this).parent().html($(this).parent().html().replace($(this).text(), $(this).text().replace($(this).text(), textHelperMultipleWords)));
}
}
})
}
}
}
}
}
});
有什么帮助吗?
谢谢!
编辑:我对任何解决方案都持开放态度。是否有一种方法可以在外部服务器上运行代码,然后替换整个文档?可以对代码进行一些改进,例如
- 将值提取到变量中,而不是一次又一次地调用函数
- 您还可以使用更简洁的正则表达式作为起始字符黑名单,并使用短语数组来排除。
- 另一个选择是在遍历大数组之前检查
helperElement
(父元素)是否有效,而不是检查父元素是否对数组中的每个单词都有效。checkGlobalWords
不需要先遍历每个单词,然后在替换时再遍历每个单词-只需无条件替换。- 而不是做
.includes
测试,然后替换,如果返回true,考虑无条件替换-节省你的一步。- 分支通常是缓慢的。不是在基于索引的大循环中使用if-else,而是只对数组的第一个元素执行第一个测试,然后对数组的其余元素执行第二个测试。或者,因为它们看起来几乎做同样的事情,除了第一个初始化
textHelperMultipleWords
-只需在循环外先初始化该变量。- 对于最后一个索引也是一样的——不是检查索引是否是最后一个,而是在循环结束后放入该语句。
- 因为看起来你在大数组中唯一关心的是对象的
word
属性,你可以先将这些词提取到它们自己的数组中,以避免每次都要在对象中导航。- 在最后替换文本看起来非常复杂-为什么不直接将新文本分配给正在迭代的元素?
const wordsToHighlight = wordsHighlight.map(obj => obj.word);
const badStartingCharacters = /[.{@!( :<^n[]/;
const badPhrases = ['document.', 'html', 'var '];
$("*").contents().each(function () {
if (this.nodeType !== 3) {
return;
}
const text = $(this).text().trim();
if (!text || badStartingCharacters.test(text[0]) || badPhrases.some(phrase => text.includes(phrase))) {
return;
}
const parent = $(this).parent()[0] as HTMLElement;
if (!parent?.outerHTML) {
return;
}
if (parent.outerHTML.includes('<title')) {
return;
}
if (parent.id === 'global-solution' || ['TITLE', 'SCRIPT', 'STYLE'].includes(parent.tagName)) {
return;
}
let newText = text;
wordsToHighlight.forEach((word) => {
newText = newText.replace(word, `<span id="global-solution" style="background-color: #ebf5f9; cursor: pointer; font-style: italic; text-transform: capitalize;">${word}</span>`);
});
if (newText !== text) {
$(this).text(newText);
}
});
你也可以通过抛弃jQuery来使事情更快一点——它在这里没有给你带来任何好处,而原生DOM方法更快。
话虽如此,如果它仍然太慢,这可能不适合客户端。对于普通浏览器来说,有太多的单词无法处理。是的,考虑在服务器上执行所有这些逻辑(只有一次,当页面更改时),然后将更改后的HTML发送给客户端,而不是让客户端每次发出请求时都这样做。