Search code examples
javascriptweb-component

How to use querySelector of Tags inside a Web Component


I am building an application with Web Components and only vanila javascript. I want to use vaadin-router for routing.

In my index.html I only display the web component app.module:

<!DOCTYPE html>
    <body>
        <mp-app-root></mp-app-root>
    </body>
</html>

the app module is a simple web component. It should display the routing module in a template. The Shadow DOM is optional.

app.module.js

import Template from './app.module.template.js'

class AppModule extends HTMLElement {
    connectedCallback() {
        this.innerHTML = Template.render();
    }
}

customElements.define('mp-app-root', AppModule)

The template simply renders the tag, where the routing should happen.

app.module.template.js

import './routing.module.js'

export default {
    render() {
        return `${this.html()}`;
    },

    html() {
        return `<script type="module" src="./routing.module.js"></script>
                <app-routing-module></app-routing-module>`;
    },
}

As you can see, i am importing the script routing.module.js - It also works, when im console.logging something

Now, I am using vaadin-router for routing and I want to access the <app-routing-module>-Tag with querySelector like this:

const outlet = document.querySelector('mp-app-root').querySelector('app-routing-module')

but it is always null.

console.log(document.querySelector('mp-app-root') works and prints the following:

<mp-app-root>
    <app-routing-module></app-routing-module>
</mp-app-root>

Solution

  • You get null, in Webkit based Browsers (Safari, Chromium) because:
    you can't access the DOM it just created, in the connectedCallback
    The Elements DOM isn't fully parsed yet.

    This is considered the correct implementation of the W3C Web Components standard

    this.innerHTML USED TO have its content in FireFox, Mozilla fixed this in 2019

    See:

    Correct code:

    setTimeout( () => {
      const outlet = document.querySelector('mp-app-root')
                             .querySelector('app-routing-module')
    });
    

    All WebComponent libraries do something similar under the hood;
    delay execution till the Eventloop is empty, and the DOM is ready to accept your selections/additions.


    Note: querySelector takes a selector, so you can write:

      const outlet = document.querySelector('mp-app-root app-routing-module')