Search code examples
javascriptweb-componentcdnes6-modulescustom-element

Customizable built-in elements on Safari – polyfill via script tag vs ES6 import


I'm building a website with using only native, vanilla JS features (ES6, web components, import modules).

I'd like to include a polyfill to get Safari to support extending HTML elements (class MyLink extends HTMLAnchorElement). Ideally, I'd like to include it via an import in my main.js file, and not as a <script> tag in my index.html.

I first tried the official Custom Elements V1 polyfill, including it both via a script tag (<script src="https://unpkg.com/@webcomponents/custom-elements"></script>) as well as via import (import "https://cdn.skypack.dev/@webcomponents/custom-elements";). There are no errors either way, but I'm not seeing support for extending built-in elements on Safari.

I tried a different polyfill that explicitly mentions customizable built-in elements, and in this case, adding it via a script tag does make it work on Safari:

<script src="//unpkg.com/@ungap/custom-elements"></script>

But it still doesn't work if I import in in my js:

import ungapCustomElements from "https://cdn.skypack.dev/@ungap/custom-elements";

Am I using the wrong CDN? The wrong polyfill? What's the difference? Why does it work via a script tag but not as an ES6 import?

In case it's relevant, here's my declaration for the custom element I'm trying to get working:

class ButtonLink extends HTMLAnchorElement {
  connectedCallback() {
    console.log("This is not called on Safari");
  }
}

customElements.define("button-link", ButtonLink, { extends: "a" });

Solution

  • This turned out to be an import ordering issue (thank you @AndreaGiammarchi for linking to the GitHub issue).

    With the polyfill included via a script tag, my scripts and imports were ordered as follows:

    <script src="//unpkg.com/@ungap/custom-elements/es.js"></script>
    <script type="module" src="/components/ButtonLink.js"></script> <!-- <-- The component was defined here, along with the call to customElements.define() -->
    <script type="module" src="/main.js"></script>
    

    With the polyfill included via import, I was trying to get it to work with the import statement added to the top of main.js (which was loaded after ButtonLink.js and didn't have any effect).

    Importing the polyfill at the top of my custom element script file fixed it.