asv
asv

Reputation: 3802

Web components in vanilla JavaScript

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()?

Upvotes: 1

Views: 585

Answers (3)

connexo
connexo

Reputation: 56813

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();  
});

Upvotes: 0

Supersharp
Supersharp

Reputation: 31219

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>

Upvotes: 1

tony19
tony19

Reputation: 138696

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)

Upvotes: 1

Related Questions