Reputation: 311
This is my first Chrome extension. I am building an extension to replace text on a page.
the provided code works great on anything loaded by DOMContent loaded. However on pages that use some sort of infinite scrolling, when new content is loaded text replacement doesn't happen.
I need it to run text replacement on any content that is added to the page later.
I have tried to use an onscroll event listener, but that is awkward and costly. I'm sure there is a better way.
I have looked into using the manifest run_at but haven't been able to find a way to use that.
let elements = document.getElementsByTagName('*');
let replacements = {
"first to replace": "new text",
"second to replace": "also new text"
};
let keys = Object.keys(replacements);
RegExp.quote = (str) => {
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
};
let makeItAwesomer = () => {
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
let node = element.childNodes[j];
if (node.nodeType === 3) {
let text = node.nodeValue;
let newText = text;
if(keys.some(function(key){
return ~text.toLowerCase().indexOf(key.toLowerCase());
})){
keys.forEach( key => {
let regex = new RegExp(RegExp.quote(key), "gi");
newText = newText.replace(regex, replacements[key]);
});
if (newText != text) {
element.replaceChild(document.createTextNode(newText), node);
}
}
}
}
}
}
makeItAwesomer();
Upvotes: 6
Views: 1723
Reputation: 138
You can use the built-in MutationObserver to detect when the DOM has changed, and can even customize it to only detect changes to the DOM structure (such as new nodes getting added on infinitely scrolling pages) and ignore other changes (such as attributes changing, which can happen at times by just hovering your mouse over something). You could have something like this:
const targetNode = document.body;
// Options for the observer (which mutations to observe)
// Set attributes to false if you do not care if existing nodes have changed,
// otherwise set it true.
const config = { attributes: false, childList: true, subtree: true };
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
makeItAwesomer();
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
This may work with your code as it is written right now, as long as makeItAwesomer()
correctly detects which text has already been modified (which it should if it works with the onScroll event).
However, to make it more efficient, if you re-write your makeItAwesomer()
function to take parameters of the nodes you want changed, you could modify the callback to have it just update the newly loaded nodes. So callback
would be more like:
const callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
makeItAwesomer(mutation);
}
};
Here's the MDN Web docs on MutationObserver to help better understand how it works.
Upvotes: 9