Search code examples
javascripthtmlweb-componentcustom-element

Why does the `is` attribute only work on web components that override a built-in element?


The is="" attribute is a way to bless a built-in element with additional smarts from a custom element, but it appears to only work if your custom element's JS implementation extends the built-in element you are using it on.

It seems to me that it would be really useful to define additional functionality as a custom element and then apply it to any built-in element. This would allow users of web components to benefit from enhanced behavior while retaining the meaning of standard semantic elements.

What is the reason for this limitation?

Firefox does appear to allow an is="" upgrade on built-in elements (https://codepen.io/bdhowe/pen/QRLrWQ?editors=1010), but other browsers I've tried do not, and the MDN docs say that it can only be used if the custom element "extends the element type it is being applied to".

class MyElement extends HTMLElement {
  constructor() {
    super();
    var shadow = this.attachShadow({mode: 'open'});
    const span = document.createElement('span');
    span.textContent = "Hello, world!";
    shadow.appendChild(span);
  }
}

customElements.define('my-elem', MyElement);
/* I want this to render "Hello, world!" twice. */
<div is="my-elem"></div>
<my-elem></my-elem>

Reference:


Solution

  • There are two ways to use the custom element definition, and what you are observing is the difference between the two.


    If you would like to have a custom element used as its own element, such as

    <my-elem></my-elem>
    

    Then you need to use extends HTMLElement. This is what currently results in "Hello, World!" for your example.


    If you would like to have a custom element used with is="", such as

    <div is="my-elem"></div>
    

    Then you need to use extends HTMLDivElement as well as to specify in the definition customElements.define('my-elem', MyElement, {extends: 'div'});


    These are two different approaches to use, and do not mix. I would assume the reason they cannot mix is due to the fact that having both work would require multiple inheritance and that isn't supported.

    It may be possible to hack something together though, although it would probably be rather unsightly. Best practice would be to use one or the other.