2

I am trying to access any element on YouTube from a content script.

manifest.json

"content_scripts": [
    {
      "run_at": "document_end",
      "matches": ["*://www.youtube.com/watch?v=*"],
      "js": ["content/content.js"]
    }
  ]

However, when logging the element, I sometimes get null and sometimes the element is logged.

content.js

document.addEventListener('DOMContentLoaded', afterDOMLoaded)

function afterDOMLoaded() {
    const element = document.getElementById('top')
    console.log(element)
}

It works when using setTimeout

setTimeout(function(){ 
    const element = document.getElementById('top')
    console.log(element)
}, 3000)

I read here that this is because the element is being added later dynamically, by the page's javascript, and the only way to solve this is by using a MutationObserver/mutation-summary. This seems like a lot of hassle for just accessing an element. Isn't there any other way?

Junker
  • 397
  • 3
  • 13
  • You could check if an element has appeared in the DOM using `setInterval` but I doubt that it's a better approach. – G07cha Dec 07 '17 at 06:23

2 Answers2

2

By Loading asynchronously we will get the solution run at "document_idle". In the case of "document_idle", the browser chooses a time to inject scripts between "document_end" and immediately after the window.onload event fires.

Update manifest.json

"content_scripts": [ { "run_at": "document_idle", "matches": [ "*://www.youtube.com/watch?v=*" ], "js": [ "content.js" ] } ]

In Content script, get the document ready state and do with asynchronous way.

HTMLDocument.prototype.ready = new Promise(function (resolve) {
if (document.readyState != "loading")
    return resolve();
else
    document.addEventListener("DOMContentLoaded", function () {
        return resolve();
    });
});

document.ready.then(function () {
    const element = document.getElementById('top')
    console.log(element)
});

Hope this helps.

Manoj
  • 2,000
  • 2
  • 16
  • 21
  • Tried this, didn't work. Probably because the page loads the elements dynamically/async? So the element that I want to access is not loaded when the events fire? (document.readyState and "DOMContentLoaded") – Junker Dec 07 '17 at 19:08
  • 1
    @Junker, I have tried it it work, I did it by having the background script injecting the js page. like this ``` if ('complete' !== changeInfo.status) { chrome.tabs.executeScript(tabId, { file: 'board.js' }, () => console.log('complete') ) }``` – Wadson Vaval Mar 20 '23 at 12:18
0

Try DOMSubtreeModified, which fires each time DOM is modified:

document.addEventListener("DOMSubtreeModified", function(event){
        if(document.getElementById("my_element")) {
                //element is added
        }
});
Hervera
  • 584
  • 9
  • 17