Chrome extension is the best way to reduce load times when using content scripts
I am creating a chrome extension that is supposed to go through an array of more than 12,000 values ββand compare it to webpage tags p
, and if the value from the array is inside a tag p
, it should highlight text and should display some information when it hovers. So, it's easy enough to assume that using content scripting, but using "run_at": "document_end"
webpage loading is 3x slower, almost 10 seconds to load with my extension. Obviously not perfect.
This is the code for my content script by the way:
jQuery(document).ready(function($) {
var $all_p = $("p");
$.each(arrayObj, function(key, value) {
$all_p.highlight(key, {caseSensitive: true, className: 'highlight-882312', wordsOnly:true });
});
$('.highlight-882312').each(function() {
var currentKey = $(this).text();
console.log(currentKey);
//Create tooltip on hover.
Tipped.create(this, "<iframe id='cardiFrame' src='https://mywebsite.com/word?q="+footballers[currentKey]+"'></iframe>", {
skin: "white",
hook: 'rightmiddle',
maxWidth: 306,
maxHeight: 365,
background: '#eee',
});
});
});
Anyway, it was too slow for me, but I still wanted to create an extension, so I decided to send an array of all tags p
to my background / popup so that in the background / popup to find the keywords and then display the keywords. which match in the popup, and send a message back to the content script that the keywords match so that they can be highlighted. Will it reduce loading times? Is this a good job for my problem, as an extra load time of 10 seconds is not ideal?
I would really appreciate any advice.
Edit: arrayObj:
var arrayObj = {
"word1": 'word_word1',
"word2": 'word_word2',
"word3": 'word_word3',
// extra 12K lines...
}
manifest.json:
My manifest is pretty typical, but the content information of the script is:
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"css": ["styles.css", "tipped.css"],
"js": ["jquery.min.js", "jquery-highlight1.js", "spinners.min.js", "tipped.js", "word.js", "script.js"],
"run_at": "document_end"
}
],
source to share
Update http://jsfiddle.net/r4bnxemL/3/
This new approach is less complex and does not combine every word into one big regex. Instead, it just uses the asycn looping method using the setTimeout trick.
var words = ['dolor', 'sit', 'odio'];
var ele = document.getElementById('text'),
html = ele.innerHTML;
asycnIterator(words,
function (word) {
var reg = new RegExp('\\b' + word + '\\b', 'ig');
html = html.replace(reg, '<span class="marked">' + word + '</span>');
}, function () {
ele.innerHTML = html;
});
function asycnIterator(arr, func, cb) {
var i = 0;
nextCall();
function nextCall() {
func(arr[i++]);
if (i >= arr.length) {
if (cb) cb();
return;
}
setTimeout(nextCall, 1);
}
}
First of all, two things you could do, break the loop into tiny pieces first, this will probably make the whole process complete a bit in a longer time, but it won't block everything else, Javascript is single threaded, so use an event loop.
You can do this for example with setTimeout. (note must use reverse pattern as it will return immediately, next line execution will not wait for loop to complete)
var arr = [1,2,3....100000];
var i = 0 ;
nonBlockingAsycnLoop(); //will prints all values in console without blocking
function nonBlockingAsycnLoop() {
console.log(arr[i++]);
setTimeout(nonBlockingAsycnLoop,0);
}
The second thing you can do is speed up your search. In this regard, the more natural the method you use, the better. This is just my approach
- concatenate all elements of an array into a string
- then search using them as regex
- save indexes
This next function does this and calls cb with a list of all occurrences.
function returnOccurences(str, arr, cb) {
var ele = null,
index = 0,
occurences = [],
regexp = '\\b' + arr.join('\\b|\\b') + '\\b';
getNextWord();
function getNextWord() {
ele = str.substr(index).match(new RegExp( regexp , 'i'));
if (!ele) {
cb(occurences);
return;
}
occurences.push({i: ele.index, len: ele[0].length, word: ele[0]});
index = ele[0].length + ele.index;
setTimeout(getNextWord,0); //makes it non blocking
}
}
The string matching function docs MDN LINK This, if not called with the g parameter for the regular expression, than the returned array with non-enumerable properties that are the index of the word found and the input that contains the original text.
We can use the index to further parse the string after the first match.
source to share