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);
})
I later wrote a (very) long Dev.to blogpost about the connectedCallback
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 (lightDOM) 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!");