-1

SOLVED: The problem was that I was not properly communicating between files. The popup.js was not sending TabId information, and also popup.js was waiting for a response that never came! It is working now :)

I am building my first Chrome extension. The feature set will be bigger, but for the moment, I am trying to get an HTML element from the active tab. The tab will always be a greenhouse webpage.

For example, this job from GoDaddy: https://boards.greenhouse.io/godaddy/jobs/5681152003?gh_jid=5681152003#app

All these kinds of links have an HTML element with id="content". This division includes paragraphs and unordered lists mainly. I just want to print this content in my popup.html. It does not work, no matter how much I try.

I have some hypotheses on where the code could go wrong (but I was unable to prove it):

- Problem with finding the : Maybe I should not use the document.querySelector("#content").querySelectorAll("p, ul > li") method to find the content I want. There might be other html elements inside the division apart from

and (like


) and this is causing trouble.

- Problem with handling the information inside : The code in content.js fetches the information inside the as a NodeList, and then iterates through that NodeList to push text content into the requirements array. Maybe this is not the correct way of doing this.

- Async communication: I had to make the functions async to handle the asyn nature of chrome.runtime.sendMessage() method. In my code, all communications between the popup.js and content.js are done through the background.js file. I suspect that there might be some problem there, but I can not understand what it is.

- Problem with Tab information: I don't know if I am sending the Tab information correctly to the content.js.

Please help! I am losing many hours of sleep for this! (Literally, I dreamt about it). Do you see any logical error in my code?

My code is the following:

**manifest.json ** Notice that I added "run_at": "document_end" inside the "content_script" to try to handle timing issues.

{
"manifest_version": 3,
"name": "Experience Tracker",
"version": "1.0",
"description": "Track your work experiences and match with job requirements.",
"permissions": [
  "activeTab"
],
"background": {
  "service_worker": "background.js"
},
"content_scripts": [
  {
    "matches": ["<all_urls>"],
    "js": ["content.js"],
    "run_at": "document_end"
  }
],
"action": {
  "default_popup": "popup.html",
  "default_icon": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  }
}

**popup.html **

<!DOCTYPE html>
<html>
<head>
  <title>Experience Tracker</title>
  <script src="popup.js"></script>
</head>
<body>
  <!-- Button that triggers the fetching of job requirements -->
  <button id="fetchRequirements">Fetch Job Requirements</button>
  <hr>
  <h2>Matched Requirements</h2>
  <!-- Paragraph element where all the content from <div id="content"> from the Active Tab will be displayed -->
  <p id="matchedRequirements"></p>
</body>
</html>

**popup.js ** The popup.js code defines a function called fetchRequirements that fetches job requirements from the background script. When the user clicks the button fetchRequirements in the Chrome extension popup, this function is triggered. The code sends a message to the background script, requesting the matched requirements. Upon receiving the response, which should include the content inside the , it generates HTML elements for each requirement and displays them in the from popup.html. The code also ensures that the fetching process aligns with the asynchronous nature of the extension environment and handles button click events.

const fetchRequirements = async () => {
    const request = {
    action: "fetchRequirements"
    };
    
    const response = await chrome.runtime.sendMessage(request);
    
    if (response.matchedRequirements) {
        const requirementsHtml = response.matchedRequirements.map(requirement => {
          return `<div>${requirement}</div><br>`;
        }).join("");
        document.getElementById("matchedRequirements").innerHTML = requirementsHtml;
      }
    };
     
    document.addEventListener("DOMContentLoaded", function() {
        const fetchRequirementsButton = document.getElementById("fetchRequirements");
        const matchedRequirementsList = document.getElementById("matchedRequirements");
    
        fetchRequirementsButton.addEventListener("click", async () => {
            await fetchRequirements();
        });
    });

**content.js ** This content.js script is responsible for extracting the job requirements from the content of the webpage. It finds the element with the ID "content" and looks for paragraphs (

) and list items (

  • ) within it. It then iterates through these elements, extracts their text content, and adds it to the requirements array. Finally, it sends a message to the background script, passing along the extracted requirements using the "fetchRequirements" action identifier.
  • function fetchRequirements() {
        const requirements = [];
        const contentElements = document.querySelector("#content").querySelectorAll("p, ul > li");
        for (const contentElement of contentElements) {
          requirements.push(contentElement.textContent);
        }
      
        chrome.runtime.sendMessage({
          action: "fetchRequirements",
          matchedRequirements: requirements
        });
      }
    

    **background.js **

    chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
        if (request.requirements) {
          chrome.storage.local.set({ matchedRequirements: request.requirements });
        }
      });
    

    I expected that when the button fetchRequirements is clicked, all the content in the active tab under the division with id="content" would be displayed in my paragraph with id="matchedRequirements". Now, when I click the fetchRequirements button, nothing happens, and I get the Error:

    Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'matchedRequirements')

    Meri
    • 1
    • 1
    • 1) chrome.runtime.sendMessage doesn't send to content script, you need chrome.tabs.sendMessage with a tab id obtained via chrome.tabs.query. 2) You need to [re-inject content scripts explicitly](/q/10994324) after reloading/installing the extension or it may be better to remove `content_scripts` and just always use executeScript as shown [here](/a/67227376). – wOxxOm Aug 30 '23 at 01:37

    0 Answers0