javascriptclasscustom-element

Why can't I extend from a custom HTML element class conditionally?


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>


Solution

  • 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>