Search code examples
javascriptcustom-element

Unable to instantiate my custom element dynamically through its default constructor


I have created a custom element that I want to use on my website, when I declare it in html document the constructor gets called and the element is instantiated and displayed withou any error. But when I want to instantiate it in code it seem to be missing a reference to its constructor using new CustomElement() .

<html>
<head>

<!-- 
    ...
-->

<script src="/custom_elements.js" defer ></script>   

<!-- 
    ...
-->


<style>



</style>



<script >


    function init(){

        var ele = new CustomElement();
        // !! does not work



    }



</script>

</head>


<body onLoad="init();" >

<custom-element></custom-element>
<!-- works -->

</body>
</html>

// within custom_elements.js the class is defined as

class CustomElement extends HTMLElement {

    constructor() {
        super();

        // code that gets displayed flawlessly when instantiated from html 


    }



}


customElements.define('custom-element', CustomElement );

Error says: "CustomElement not defined"..

So it seems that there is some reference missing because when I paste the definition of my CustomElement inside the tag where my code is then it works.. So how do I load the file that my script has reference to it as well, not only HTML code.. Thank you for any advice on how these things work

EDIT/REASON OF THE ERROR

So it seems there is logically nothing wrong with this implementation but my custom_elements.js file had an if/else clause within which the elements were defined.. It was

if(!customElementsSupported){

    // custom elements not supported action

}else{

    // definitions of custom elements

}

I assume that this must have put the custom elements inside a local scope thus preventing them to be accessible for my functions


Solution

  • As I stated in the comments you can get rid of defer in your <script> tag.

    Or you just need to know when the class is available by using customElements.whenDefined(name);

    If you make this change in your init function it will work:

    function init(){
      let ele;
      customElements.whenDefined('custom-element').then(
        function() {
          ele = new CustomElement();
        }
      );
    }

    This waits for the element to be defined, which will not happen until the class exists. So you are then safe to instantiate your element any way you want.

    UPDATE

    You can also create your object using document.createElement but you still have to wait until the element is defined.

    function init(){
      customElements.whenDefined('custom-element').then(
        function() {
          let ele = document.createElement('custom-element');
          // Do things with your element
        }
      );
    }

    FURTHER UPDATE

    I created two file:

    custom_elements.js

    class CustomElement extends HTMLElement {
      constructor() {
        super();
        alert('constructor');
      }
    
      connectedCallback() {
        alert('connectedCallback');
      }
    }
    
    customElements.define('custom-element', CustomElement);
    

    And test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <script src="custom_elements.js" defer></script>
      <script>
      function init() {
        customElements.whenDefined('custom-element').then(
          function () {
            let ele = new CustomElement();
            document.body.appendChild(ele);
          }
        );
      }
      </script>
    </head>
    <body onLoad="init();">
    </body>
    </html>
    

    And this works just fine. I get the two alerts just like I expect them.