0

So I am trying to make a chrome extension that adds a panel to the Elements devtool and got something very simple to work. But when I tried setting the page to something with some more intracate JavaScript I get Uncaught TypeError: Cannot read properties of null errors.

Here is what I had before: HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="panel.js"></script>
    <title>Document</title>
</head>
<body>
    <p id="xpath_display"> Welcome, right click an element and click XPathGenerator to start </p>
</body>
</html>

JS

chrome.runtime.onMessage.addListener((message, sender, responce) => {
    if (message.request === "sendtodevtools"){
        buildUI(message.path);
    }
})

function buildUI(data) {
    let display = document.getElementById("xpath_display");
    display.textContent = data;
}

and this worked fine. But when I tried to add tabs with some js attached like so...

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="panelstyles.css"></link>
    <script src="panel.js"></script>
    <title>Document</title>
</head>
<body>
    <div class="tab">
        <button class="tablinks" onclick="openTab(event, 'XpathTab')">Xpath Display</button>
        <button class="tablinks" onclick="openTab(event, 'PageTab')">Page Generation</button>
        <button class="tablinks" onclick="openTab(event, 'TestTab')">Test Generation</button>
    </div>

    <!-- Xpath Tab Content -->
    <div id="XpathTab" class="tabcontent">

        <div style="padding: 10px;">
            <label for="element_name">Element Name:</label>
            <input id="element_name" onclick="hideNoNameError()"></input>
            <p id="no_name_error" style="display: none;">Error, must have name to add</p>
        </div>
        
        <div id="xpath_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="xpath_display" style="display:flex;flex-direction:column;">UniqueXpath</label>
            <p id="xpath_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>

        <div id="id_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="id_display" style="display:flex;flex-direction:column;">UniqueID</label>
            <p id="id_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>

        <div id="QA_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="QA_display" style="display:flex;flex-direction:column;">QA ID</label>
            <p id="QA_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>
    </div>

    <!-- Page Tab Content -->
    <div id="PageTab" class="tabcontent">
        <p id="page_display">Nothing here yet!</p>
    </div>

    <!-- Test Tab Content -->
    <div id="TestTab" class="tabcontent">
        <p id="page_display">Nothing here yet!</p>
    </div>
</body>
</html>

JS

function openTab(evt, tabName) {
  // Declare all variables
  var i, tabcontent, tablinks;

  // Get all elements with class="tabcontent" and hide them
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }

  // Get all elements with class="tablinks" and remove the class "active"
  tablinks = document.getElementsByClassName("tablinks");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }

  // Show the current tab, and add an "active" class to the button that opened the tab
  document.getElementById(tabName).style.display = "block";
  evt.currentTarget.className += " active";
}

/*
  Copies text to clipboard
*/
function copy(id) {
  var text = document.getElementById(id).textContent;
  navigator.clipboard.writeText(text);
}

//Sets no name error to be hidden
function hideNoNameError(){
  document.getElementById("no_name_error").style.display = "none";
}

chrome.runtime.onMessage.addListener((message, sender, responce) => {//Recieves message from script.js (method CreateXpath)
  if (message.request === "sendtodevtools"){
      buildUI(message.path, message.id);
  }
})

function buildUI(xpath, id) {//Updates the text in the displays to their 1 of 1 mappings
  let xpath_display = document.getElementById("xpath_display");
  xpath_display.textContent = xpath;
  let id_display = document.getElementById("id_display");
  id_display.textContent = id;
}

I got a CSP error so I changed it again to this...

<body>
    <div class="tab">
        <button id="xpath_tab_button" class="tablinks">Xpath Display</button>
        <button id="page_tab_button" class="tablinks">Page Generation</button>
        <button id="test_tab_button"class="tablinks">Test Generation</button>
    </div>

    <!-- Xpath Tab Content -->
    <div id="XpathTab" class="tabcontent">

        <div style="padding: 10px;">
            <label for="element_name">Element Name:</label>
            <input id="element_name" onclick="hideNoNameError()"></input>
            <p id="no_name_error" style="display: none;">Error, must have name to add</p>
        </div>
        
        <div id="xpath_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="xpath_display" style="display:flex;flex-direction:column;">UniqueXpath</label>
            <p id="xpath_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>
        <div id="id_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="id_display" style="display:flex;flex-direction:column;">UniqueID</label>
            <p id="id_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>
        <div id="QA_display_container" style="padding: 10px; border: 2px solid black;">
            <label for="QA_display" style="display:flex;flex-direction:column;">QA ID</label>
            <p id="QA_display"> Welcome, right click an element and click XPathGenerator to start </p>
        </div>
    </div>

    <!-- Page Tab Content -->
    <div id="PageTab" class="tabcontent">
        <p id="page_display">Nothing here yet!</p>
    </div>

    <!-- Test Tab Content -->
    <div id="TestTab" class="tabcontent">
        <p id="page_display">Nothing here yet!</p>
    </div>
</body>
</html>

JS

document.getElementById("xpath_tab_button").addEventListener("click",openTab(event, 'XpathTab'));
document.getElementById("xpath_tab_button").addEventListener("click",openTab(event, 'PageTab'));
document.getElementById("xpath_tab_button").addEventListener("click",openTab(event, 'TestTab'));

function openTab(evt, tabName) {
  // Declare all variables
  var i, tabcontent, tablinks;

  // Get all elements with class="tabcontent" and hide them
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }

  // Get all elements with class="tablinks" and remove the class "active"
  tablinks = document.getElementsByClassName("tablinks");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }

  // Show the current tab, and add an "active" class to the button that opened the tab
  document.getElementById(tabName).style.display = "block";
  evt.currentTarget.className += " active";
}

but now I am getting Uncaught TypeError: Cannot read properties of null (reading 'addEventListener') on this line:

document.getElementById("xpath_tab_button").addEventListener("click",openTab(event, 'XpathTab'));

why is this coming back as null? document.getElementById("xpath_tab_button")

1 Answers1

0

Ope I am dumb and it looks like I needed to wait until the DOM was loaded before adding those listeners. This fixed it:

window.onload = function(){
  document.getElementById("xpath_tab_button").addEventListener("click",openTab(this, 'XpathTab'));
  document.getElementById("xpath_tab_button").addEventListener("click",openTab(this, 'PageTab'));
  document.getElementById("xpath_tab_button").addEventListener("click",openTab(this, 'TestTab'));
}