Technologies:
Tolerim
a month ago
Sometimes, MutationObserver doesn't work without opening the Developer Toolbox.
I developed a Chrome Extension that changes or eliminates specific elements on a page as it loads. The issue I'm encountering is that the condition inside contentScript.js
sometimes fails when I reload the page. It works when I reload the extension or when the Chrome Developer Toolbox is opened. Additionally, it functions correctly when I hard reload the page. Even when the condition above isn't triggered, toListen always includes "elementsTable" when I print it. What could be causing this inconsistency? Here is the contentScript.js code:if (toListen.includes("elementsTable"))
let mo = new MutationObserver(onMutation);
//chrome.storage contains options["elementsTable"]
async function retrieveStorage(key) {
return new Promise((resolve, reject) => {
chrome.storage.sync.get(key, resolve);
})
}
function onMutation(mutations) {
for (const { addedNodes } of mutations) {
for (const n of addedNodes) {
if (n.tagName) {
if (toListen.includes("elementsTable")) {
if (n.tagName == 'TABLE' && $(n).attr("bgcolor") == "#555576") {
console.log("doing something")
}
}
}
}
}
}
function observe() {
mo.observe(document, {
subtree: true,
attributes: true,
childList: true
});
}
function listenThread() {
onMutation([{ addedNodes: [document.documentElement] }]);
observe();
};
async function start() {
await retrieveStorage()
.then((result) => {
$.each(result.options, function (option) {
toListen.push(option);
})
listenThread();
})
};
start();
And here is the manifest.json:
{
"name": "xxxxx",
"version": "xxx",
"description": "xxxx",
"permissions": ["storage"],
"host_permissions": [
"https://*.xxxx.com/*",
"https://*.xxxx.com/*"
],
"content_scripts": [
{
"matches": ["https://*.xxxx.com/*"],
"runat": "documentstart",
"all_frames": true,
"js": ["/static/thirdParty/jquery-3.6.3.min.js", "/static/contentScript.js"]
}
],
"background": {
"service_worker": "static/background.js"
},
"action": {
"manifest_version": 3
}
Answers(2)
Vogi
a month ago
Verified Answer
The condition inside contentScript.js fails sometimes when you reload the page. It works when you reload the extension or when the Chrome Developer Toolbox is opened. Additionally, it functions correctly when you hard reload the page. If you print toListen, it always includes "elementsTable" even when the condition above isn't triggered.
This inconsistency could be caused by a number of factors such as caching issues or timing issues. One possible solution is to use a background script to communicate with the content script and send messages to it when the page is loaded or reloaded. You can also try using a different event listener such as window.onload instead of MutationObserver.
Here are some links that could help you troubleshoot this issue:
- Chrome extension content script not loading - Stack Overflow
- MutationObserver fails sometimes unless the Developer Toolbox is opened - Stack Overflow
- Chrome extension: Checking if content script has been injected or not - Tagmerge
Sources
Chrome extension content script not loading - Stack Overflowjavascript - MutationObserver fails sometimes unless the Developer ...javascript - Chrome extension: Checking if content script has been ...Chrome extension Content Script not loaded until page is refreshedTolerim
a month ago
The issue might be related to the timing in which your contentScript.js runs or even with the MutationObserver itself. If the issue persists only when the page is reloaded, it might be that your contentScript.js is not running early enough in the process and the MutationObserver is not yet observing the elements you are interested in.
One possible solution is to move the code that retrieves the options from chrome.storage to the background.js file and pass the retrieved options as a message to the content script when it loads. This way, you can ensure the content script has all the options it needs before it starts observing the mutations.
Here's an example of how you could modify your code:
background.js
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.get("options", (result) => {
chrome.tabs.query({}, (tabs) => {
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, { type: "options", options: result.options });
}
});
});
});
contentScript.js
let toListen = [];
function onMutation(mutations) {
for (const { addedNodes } of mutations) {
for (const n of addedNodes) {
if (n.tagName) {
if (toListen.includes("elementsTable")) {
if (n.tagName == 'TABLE' && $(n).attr("bgcolor") == "#555576") {
console.log("doing something")
}
}
}
}
}
}
function observe() {
const mo = new MutationObserver(onMutation);
mo.observe(document, {
subtree: true,
attributes: true,
childList: true
});
}
chrome.runtime.onMessage.addListener((message) => {
if (message.type === "options") {
toListen = Object.keys(message.options);
observe();
}
});
In this example, we have moved the mutationObserver creation to a separate function called observe and we are now calling it only when we receive the options from the background.js file. We have also removed the start function and the retrieveStorage function since we are getting the options from the background instead.
Note that we are now listening for the options message in the contentScript.js using chrome.runtime.onMessage.addListener, and we are passing the options as an object with Object.keys to get the array of keys.
We also removed the async/await syntax since we are not doing anything async anymore.