Nolo
Nolo

Reputation: 886

custom element type reports HTMLElement when loaded in script type=module?

I'm working with custom elements and I noticed in one case I was unable to access the properties of an instance as in var prop = myCustomElement.myPropery // undefined where the property is defined in the class as e.g. get myProperty() { blah }.

I've boiled the problem down to what gets reported as in myCustomElement.constructor ( and thus the prototype/instance type ) when the custom element is defined in a script type="module" vs. without the module type.

Here's a very simple example:

var test = document.getElementById ( "test" );

console.log ( test.mood, test.constructor );
<script type="module">
const template = document.createElement ( "template" );

template.innerHTML = "<span>Hello!</span>";

window.customElements.define ( "test-case",

class TestCase extends window.HTMLElement {
    constructor () {
        super ();
        
        var frag = template.content.cloneNode ( true ),
            shadow = this.attachShadow( { mode: "open" } );
        
        shadow.appendChild ( frag );
    }
    
    get mood () {
        return "Happy!";
    }
} );
</script>
<test-case id="test"></test-case>

... and without the module type

var test = document.getElementById ( "test" );

console.log ( test.mood, test.constructor );
<script>
const template = document.createElement ( "template" );

template.innerHTML = "<span>Hello!</span>";

window.customElements.define ( "test-case",

class TestCase extends window.HTMLElement {
    constructor () {
        super ();
        
        var frag = template.content.cloneNode ( true ),
            shadow = this.attachShadow( { mode: "open" } );
        
        shadow.appendChild ( frag );
    }
    
    get mood () {
        return "Happy!";
    }
} );
</script>
<test-case id="test"></test-case>

This seems quite wrong, is this behavior intentional?

And also, assuming the need to use a module for the purpose of e.g. defining a custom element to be used inside a larger component or library, is there a straight forward way to prevent the module from hiding the type of the custom element?

Note: I'm testing this from latest Chrome (74.0.3729.157), and haven't tried other browsers.

Upvotes: 1

Views: 357

Answers (2)

Intervalia
Intervalia

Reputation: 10965

Your non-module script is running before the module script. So you simply need to wait until the element has been defined but calling customElements.whenDefined: https://devdocs.io/dom/customelementregistry/whendefined

Also there is no reason to use window when using customElements or HTMLElement.

customElements.whenDefined('test-case').then(
  () => {
    const test = document.getElementById ( "test" );
    console.log ( test.mood, test.constructor );
  }
);
<script type="module">
const template = document.createElement ( "template" );
template.innerHTML = "<span>Hello!</span>";
customElements.define ( "test-case",
  class TestCase extends HTMLElement {
    constructor () {
      super ();
      const frag = template.content.cloneNode ( true );
      const shadow = this.attachShadow( { mode: "open" } );
      shadow.appendChild ( frag );
    }
    
    get mood () {
      return "Happy!";
    }
  }
);
</script>
<test-case id="test"></test-case>

Upvotes: 1

Hyyan Abo Fakher
Hyyan Abo Fakher

Reputation: 3527

You need to wait until the element is defined.

See Custom​Element​Registry​.when​Defined()

The whenDefined() method of the CustomElementRegistry interface returns a Promise that resolves when the named element is defined.

customElements.whenDefined('test-case').then(
  () => {
    const test = document.getElementById("test");
    console.log(test.mood, test.constructor);
  }
);
<script type="module">
const template = document.createElement ( "template" );

template.innerHTML = "<span>Hello!</span>";

window.customElements.define ( "test-case",

class TestCase extends window.HTMLElement {
    constructor () {
        super ();
        
        var frag = template.content.cloneNode ( true ),
            shadow = this.attachShadow( { mode: "open" } );
        
        shadow.appendChild ( frag );
    }
    
    get mood () {
        return "Happy!";
    }
} );
</script>
<test-case id="test"></test-case>

Upvotes: 1

Related Questions