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?

upvote
  flag
You could check if an element has appeared in the DOM using setInterval but I doubt that it's a better approach. – Konstantin Azizov

1 Answers 11

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.

upvote
  flag
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

Not the answer you're looking for? Browse other questions tagged or ask your own question.