How to get a <div> HTML element from curren tab in javascript in Chrome Extension and print it

63 views Asked by At

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 <p> and <ul> (like <hr>) and this is causing trouble.

- Problem with handling the information inside <div>: The code in content.js fetches the information inside the <div> 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.

Is there 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 <div id="content">, it generates HTML elements for each requirement and displays them in the <p id="matchedRequirements"> 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 (<p>) and list items (<li>) 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')

1

There are 1 answers

0
halfer On

(Posted the solution on behalf of the question author to move it to the answer space).

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.