0

I am developing a small extension for chromium and I will do a script injection when the user loads a specific page.

manifest.json

    {
    "name" : "e-CalendarL3",
    "description": "Extension pour les L3 informatique étudiant à l'université d'Angers",
    "version": "0.0.1",
    "background" : {
        "scripts" : ["background.js"],
        "persistent": true
    },
    "permissions":["webNavigation","storage","activeTab"],
    "browser_action": {
        "default_popup": "popup.html"
    },
    "content_scripts": [
        {
        "matches": ["https://edt.univ-angers.fr/edt/*"],
        "js": ["injection.js"],
        "run_at": "document_end"   
        }
    ], 
    "manifest_version": 2
}

background.js

chrome.runtime.onInstalled.addListener(function() {
    alert('Merci d\'avoir installé l\'extension ');
})

injection.js

var aGrid = document.getElementsByClassName('fc-time-grid-event');
console.log(aGrid); // works
console.log(aGrid[0]) // don't works
console.log(aGrid.length); // don't works
console.log(aGrid); // works

injection.js result in the console enter image description here

the html collection is not empty and it is correctly displayed but the other two lines of the injection.js script cause me problems (line 3 and 4) for example line 4 should return to me 24

1 Answers1

0

There are several problems here.

Content scripts run after DOMContentLoaded (by default) but this will never guarantee that a page won't run its scripts dynamically afterwards. A lot of modern pages do that while building their UI based on callbacks for requestAnimationFrame and setTimeout or in response to a network request they make to fetch the fresh data.

getElementsByClassName returns a live collection:

  • it was empty when the script ran as evidenced by console.log(aGrid[0])
  • then your script finished
  • then a page script ran and added the elements dynamically
  • then you expanded the collection in devtools console
  • then devtools looked for elements and found them at that exact moment

Solution.

A possible solution is to use MutationObserver to wait for the elements:

let aGrid = document.getElementsByClassName('fc-time-grid-event');
if (aGrid[0]) {
  onGridAdded();
} else {
  new MutationObserver((mutations, observer) => {
    if (aGrid[0]) {
      observer.disconnect();
      onGridAdded();
    }
  }).observe(document, {childList: true, subtree: true});
}

function onGridAdded() {
  console.log([...aGrid]);
  // use aGrid here
}

More examples: link.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Is there another way to make a script that runs at the end of the page load, to modify the page? – Monsieur AZERTY Sep 22 '20 at 07:00
  • Clarify your question please. Was my explanation of the underlying cause unclear? Is there something wrong with the suggested approach? Did you see the examples linked at the end of the answer? – wOxxOm Sep 22 '20 at 08:10
  • sorry, I just got it. I didn't understand that dynamic javascript is unpredictable – Monsieur AZERTY Sep 23 '20 at 12:31