skip to Main Content

The snippet below spits out an error that CustomElementParent is not defined when I try to extend from it in CustomElementChild.

When I remove the ! customElements.get('custom-element-parent') check, it works.

In my case, CustomElementParent is in a third party script, so I can’t actually remove the if statement.

I guess this is a problem of conditionally defining a class. Is there any way to extend this class if it is conditionally defined?

<script>
if (! customElements.get('custom-element-parent')) {
    class CustomElementParent extends HTMLElement {
        constructor() {
            super();
        }
    }

    customElements.define('custom-element-parent', CustomElementParent);
}

if (! customElements.get('custom-element-child')) {
    class CustomElementChild extends CustomElementParent {
        constructor() {
            super();
            alert('test');
        }
    }

    customElements.define('custom-element-child', CustomElementChild);
}
</script>

<custom-element-child></custom-element-child>

2

Answers


  1. Scope. Classes have scope, same as variables.

    if (true) {
        let x = 5;
    }
    
    // you can’t access `x` here
    

    (We don’t talk about var. Point is, class works like let/const/function declarations in strict mode.)

    If you can’t change how CustomElementParent is scoped and you can’t put your code in its scope, you can’t access it that way. You can look it up in the custom element registry instead:

    const CustomElementParent = customElements.get('custom-element-parent');
    

    Full example:

    {
        class Hidden extends HTMLElement {
            constructor() {
                super();
                console.log('parent');
            }
        }
    
        customElements.define('custom-element-parent', Hidden);
    }
    
    const CustomElementParent = customElements.get('custom-element-parent');
    
    class CustomElementChild extends CustomElementParent {
        constructor() {
            super();
            console.log('child');
        }
    }
    
    customElements.define('custom-element-child', CustomElementChild);
    <custom-element-child></custom-element-child>
    Login or Signup to reply.
  2. Like let and const, class declarations are block-scoped. So the scope of the CustomElementParent declaration is the if block, and it’s not accessible when you get to the second if block.

    You can solve this by declaring the names outside the scope, then assigning them with class expressions.

    var CustomElementParent, CustomElementChild;
    
    if (!customElements.get('custom-element-parent')) {
      CustomElementParent = class CustomElementParent extends HTMLElement {
        constructor() {
          super();
        }
      }
    
      customElements.define('custom-element-parent', CustomElementParent);
    }
    
    if (!customElements.get('custom-element-child')) {
      CustomElementChild = class CustomElementChild extends CustomElementParent {
        constructor() {
          super();
          alert('test');
        }
      }
    
      customElements.define('custom-element-child', CustomElementChild);
    }
    <custom-element-child></custom-element-child>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search