0

Hi it's my first time using JavaScript and HTML so I might be doing something wrong:

I am trying to insert something new into a webpage using chrome extension, and my code sometimes work but sometimes not. For example, when I try to insert a text into any YouTube page, my code can querySelect id="logo" but not id="secondary". I don't have a good sense of JavaScript nor HTML, and I am completely stuck here.

Here is my manifest.json and my content.js:

{
    "manifest_version": 3,
    "name": "tutorial",
    "version": "1.0",
    "description": "Add text to YouTube page",
    "content_scripts": [
      {
        "js": ["content.js"],
        "matches": [
          "https://www.youtube.com/*"
        ]
      }
    ]
}
if(document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded',afterDOMLoaded);
} else {
  console.log('DOM fully loaded and parsed');
  
  const object = document.querySelector('#secondary');

  if (object) {
    const newElement = document.createElement('div');
    newElement.innerText = 'Hello from the YouTube Element Injector!';
    object.insertAdjacentElement('afterbegin', newElement);
  } else {
    console.log('object is null');
  }
}

and my result with #logo

and my result with #secondary

I would appreciate any advice! Thanks

maiko
  • 3
  • 1
  • See also [How to detect page navigation on YouTube and modify its appearance seamlessly?](https://stackoverflow.com/a/34100952) – wOxxOm Apr 20 '23 at 16:02

2 Answers2

1

Iy may be that the YouTube page doesn't have "#secondary" at the time your code is executed. This could happen if the element is added to the page dynamically, after your code has already run.

You can get around this by using a MutationObserver to watch for changes to the DOM

// Define a function to handle DOM mutations
function handleMutations(mutationsList, observer) {
  for (const mutation of mutationsList) {
    if (mutation.type === 'childList') {
      const obj = document.querySelector('#secondary');

      if (obj) {
        const el = document.createElement('div');
        el.innerText = 'Hello from the YouTube Element Injector!';
        obj.insertAdjacentElement('afterbegin', el);
        // you should disconnect() after insertion for performance
        observer.disconnect();
      }
    }
  }
}

// Create a new observer and start watching for mutations
const observer = new MutationObserver(handleMutations);
observer.observe(document.body, { childList: true });

This create a MutationObserver and starts watching for changes on document.body. When a child element has been added or removed (i.e. the "childList" mutation), handleMutations is called. In that function, you can try to select #secondary and insert the new div element.

Note that you should call observer.disconnect() after you've inserted the new element to stop the observer from watching for further mutations. This is because you only need to insert the new element once, and you don't want to keep inserting it every time the DOM changes.

I hope this helps! Let me know if you have any questions.

Patrick
  • 13,872
  • 5
  • 35
  • 53
  • 1
    Thanks Patrick, this saved my problem! I added a break; inside if(obj) to break out from the for loop, otherwise it inserted the new element 4 times:) – maiko Apr 21 '23 at 17:20
0

With the example you gave, I presume you are trying to explore the youtube.com DOM. The #secondary node seems to exist but not during the first render.

Maybe you can try to wait for the readystate to change

document.addEventListener("readystatechange", (event) => {
  if (document.readyState === "complete") {
    afterDOMLoaded(event);
  }
});

Here the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/API/Document/readystatechange_event

Emmanuel
  • 68
  • 8
  • Thanks for your advice, although this wasn't the direct cause of my problem, it was new and interesting to know! – maiko Apr 21 '23 at 17:24