Web components in vanilla JavaScript

547 views Asked by At

I've created a little web component without any frameworks. This is my index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <script src="clock.js" async></script>
</head>

<body>
    <clock-digital></clock-digital>
    <script>
        console.log(document.querySelector('clock-digital').cID);
        document.querySelector('clock-digital').method();
    </script>
</body>

</html>

and this is my clock.js:

customElements.define('clock-digital', class extends HTMLElement {

    get cID() {}

    set cID(value) {}

    constructor() {
        super();
        var shadowRoot = this.attachShadow({
            mode: 'open'
        });
        var that = shadowRoot;
        this.cID = setInterval(function () {
            var currentdate = new Date();
            that.innerHTML = `<div style="display: inline; background-color: whitesmoke; font-style: italic;">${currentdate.getHours()}:${currentdate.getMinutes()}:${currentdate.getSeconds()}</div>`;
        }, 500);
    }

    method() {
        console.log('method');
    }

});

The browser console shows this error:

undefined

(index):14 Uncaught TypeError: document.querySelector(...).method is not a function
at (index):14

Why can't my inline script access cID and method()?

3

There are 3 answers

2
tony19 On

Your inline script runs before clock.js is imported (which is asynchronous because of the async attribute you've added to the <script> tag). Since the element is defined in clock.js, <clock-digital>.method and <clock-digital>.cID don't yet exist when your inline script tries to access them.

A couple options:

  • Remove the async tag so that the import happens synchronously before your inline script runs (demo). You'd lose the advantage of the asynchronous load, but that might not be an issue for you.
  • Run your inline script after a timeout (allowing clock.js to finish importing)
0
Supersharp On

To keep the script asynchronous (async), which is sometimes better, you can add an onload event handler to the <script> element, that will call your inline script:

<script>
  function init() {
      console.log(document.querySelector('clock-digital').cID);
      document.querySelector('clock-digital').method();  
  }
</script>

<script src="clock.js" async onload="init()"></script>
0
connexo On

The web components API has you covered here. Simply use the customElements.whenDefined(tagName) promise here:

customElements.whenDefined('clock-digital').then(() => {
    console.log(document.querySelector('clock-digital').cID);
    document.querySelector('clock-digital').method();  
});