Skip to Content Collective Idea Home

Illegal Constructor for Custom HTML Elements

by Matt Slack

The Short Story

TL/DR: You probably have one of two problems:

  1. You forgot to register your custom element. e.g. window.customElements.define(your-tagname-here, this)
  2. You’re trying to extend something other than HTMLElement

The Long Story

A while back I was trying to create a new custom HTML Element for a project. This was not my first rodeo. I’ve made tons of custom elements. I love custom elements.

This time no matter what I did my console would spit back an Illegal Constructor error.

I spent some time googling and checking StackOverflow. Everyone was convinced the problem was I didn’t register my element. But I did.

After far too much time hitting my head against the wall I had the brilliant idea of reading the spec.

What was different from all my previous custom elements was I wanted to extend a specific HTML element. I’d heard this could be done using the is attribute, but Webkit/Safari don’t support is. I assumed instead I could still use a custom tagname:

class FancyButtonComponent extends HTMLButtonElement {
  static tagName = 'FANCY-BUTTON'

  static register () {
    if (window.customElements.get(this.tagName.toLowerCase())) return false
    window.customElements.define(this.tagName.toLowerCase(), this)
  }
}

It turns out, you can’t do that. If you’re not using the is attribute you can only extend the base HTMLElement.

Putting this out there in hopes of saving someone else some time, and frustration, in the future.

Also, here is the relevant portion of the spec, with the key sentence emphasized.

When defining our custom element, we have to also specify the extends option:

customElements.define("plastic-button", PlasticButton, { extends: "button" })

In general, the name of the element being extended cannot be determined simply by looking at what element interface it extends, as many elements share the same interface (such as q and blockquote both sharing HTMLQuoteElement).

To construct our customized built-in element from parsed HTML source text, we use the is attribute on a button element:

<button is="plastic-button">Click Me!</button>

Trying to use a customized built-in element as an autonomous custom element will not work; that is, <plastic-button>Click me?</plastic-button> will simply create an HTMLElement with no special behavior.