Search code examples
javascripthtmlpolymerweb-componentcustom-element

Dynamically loading WebComponents Polyfill before page ready


I'm working on a widget library so that a customer only has to import a single javascript file in the <head> of their document. After that file has been loaded, the user should be able to use custom elements that were loaded from that single script loaded in the head.
The trouble is I need to use the WebComponents polyfill because not all of the clients use browsers that support Custom Elements.
My current "solution" (which isn't consistent) is that I have my own bundle which:

  1. Dynamically includes the WebComponents bundle by inserting the script to load it in the <head>.
    • I would like to use the WebComponents-Loader which would make additional calls to only get the polyfills that are needed.
  2. Loads up my code which includes the custom elements.

The result should be that the customer can use any of our custom elements on their page. The problem is that when I dynamically insert the web components polyfill, it seems that the browser just keeps on going, and if the DOM is ready before the browser has finished loading/executing the web components polyfill, then the web components on the screen won't work.

Here's an example of what I'm trying to do.

//bundle-test.js
let polyfillScript = document.createElement('script');
polyfillScript.src = 'widget/webcomponentsjs/webcomponents-bundle.js';
polyfillScript.async = false;
document.head.appendChild(polyfillScript);

...
<html>
    <head>
        <script src="widget/bundle-test.js"></script>
        <!--The above script will dynamically insert the script commented below-->
        <!--<script src="widget/webcomponentsjs/webcomponents-bundle.js" async="false"></script>-->
    </head>
    <body>
        <h1>Hello world!</h1>
        <document-viewer test='food'></document-viewer>
    </body>
</html>

I've told the script to not be async (which should have solved it). But I'm seeing that the browser just continues on to the body and starts evaluating things before everything is ready.

Is there a better approach for what I'm trying to do? I'm still trying to figure out all the ins and outs of WebComponents.


Solution

  • You can wait for the polyfill to be loaded before you define your custom elements :

    //bundle-test.js
    let polyfillScript = document.createElement('script');
    polyfillScript.src = '/webcomponentsjs/webcomponents-bundle.js';
    polyfillScript.async = false;
    document.head.appendChild(polyfillScript);
    
    polyfillScript.onload = () =>
      customElements.define( 'document-viewer', class extends HTMLElement {
        connectedCallback() {
          this.innerHTML = this.getAttribute( 'test' )
        }
      } )
    

    Alternatly, if you want to use webcomponents-loader.js instead, you must also use WebComponents.waitFor:

    ...
    polyfillScript.onload = () =>
        WebComponents.waitFor( () =>
            customElements.define( 'document-viewer', class extends HTMLElement {
                connectedCallback() {
                    this.innerHTML = this.getAttribute( 'test' )
                }
            } ) 
        )