1

I want to select the icon with the id='home-i' and give at an on-click effect; however, when I use document.querySelector('left-navbar nav'); it returns as null. I am using a basic javascript custom element, which appears to be causing the problem.

I have tried using shadowRoot and using a setTimeout trick as shown in this post. Both methods didn't seem to work.

The <left-navbar></left-navbar> is the custom element.

  <body>
        <main>
            <!-- Heading -->
            <h1 id='reading-heading'>Chapter 1</h1>

            <!-- Navigation Bar -->
            <left-navbar></left-navbar>

Custom component

class Navbar extends HTMLElement {
    connectedCallback() {
        setTimeout(() => {
            this.innerHTML = `
            <nav id='navbar'>
                <i class="fas fa-home" id='home-i'></i>
                <hr class="line">
                <i class="fas fa-arrow-alt-circle-left" id='back-i'></i>
                <i class="fas fa-brain"></i>
                <i class="fas fa-lightbulb" id='quiz-i'></i>
                <i class="fas fa-sign-out-alt" id='exit-i'></i>
            </nav>
            `
        });
    }
}
customElements.define('left-navbar', Navbar)

Javascript using query selector

const homeBtn = document.querySelector('left-navbar nav');

homeBtn.addEventListener('click', () => {
    window.location.href = 'index.html';
    changePageTitle(0);
})
Void
  • 169
  • 14
  • Are you sure you're trying to add the event listener after the ` – Barmar Nov 13 '21 at 01:57
  • 1
    Why are you using `setTimeout()` to add the innerHTML? – Barmar Nov 13 '21 at 01:57
  • I was trying to follow the advice from that link. I was about getting the contents of a custom element, which is what I want to do. – Void Nov 13 '21 at 01:59
  • A custom element seems a little overkill for what is essentially a function that returns HTML formed by a template string. – Andy Nov 13 '21 at 02:01
  • 1
    If I understand the other question correctly, you only need to do this if you're trying to read the `innerHTML` of the element. – Barmar Nov 13 '21 at 02:03
  • I'm not sure how to tell if I am adding the event listener after or before the element. And I'm not sure how to guarantee the order that they are added in. – Void Nov 13 '21 at 02:03
  • Good point, it does seem that is not what I am trying to do. No wonder it didn't work. – Void Nov 13 '21 at 02:04

1 Answers1

2

That setTimeout makes its function execute after the EventLoop is cleared,
so after all other JS code is parsed.

See the code below, other code is displayed in the console, and then setTimeout is displayed.

Thus when you tried to add a Click handler, there was no HTML yet to use querySelector on.

If the click handler is tied to the Web Component, then add the click handler inside the Web Component.

<script>
  customElements.define("my-element", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => console.log("setTimeout"));

      this.innerHTML = `<nav>Element Nav InnerHTML</nav>`;
      this.querySelector("nav").onclick = (evt) => alert("clicked!");
    }
  });
  console.log("other code");
  // your addEventListener was here
</script>

<my-element></my-element>

Notes:

  • As mentioned in the comments setTimeout is required when you want to READ the Web Component innerHTML in the connectedCallback; because the connectedCallback fires on the opening tag, so its innerHTML isn't parsed yet. setTimeout executes after all DOM and (inline) JS is parsed.
    Deep dive in this SO post

  • I use an inline onclick Event listener here; addEventListener is for use cases where you (might) want to add multiple listeners on one DOM element.
    See: addEventListener vs onclick

  • You can also do: this.onclick = (evt) => alert("clicked!");

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49