Defining your element

Every custom element starts with a JavaScript class that extends HTMLElement. Here's a simple example that turns yellow when you click it:

class HighlightElement extends HTMLElement {
  constructor() {
    super();
    this.highlight = false;
    this.addEventListener("click", () => this.toggleHighlight());
  }

  toggleHighlight() {
    this.highlight = !this.highlight;
    if (this.highlight) {
      this.style.background = "yellow";
    } else {
      this.style.background = "";
    }
  }
}

Our element class has a constructor, where we can set up properties, and a method (toggleHighlight) that gets called from an event listener.

Now we'll tell the browser about it. All custom elements must have a dash in their name to keep them from accidentally overlapping with any new built-in elements that browsers might introduce, so we'll call ours "highlight-element" and tell the browser to associate it with this class:

window.customElements.define("highlight-element", HighlightElement);

Once defined, any existing <highlight-element> tags in the page will be upgraded, and any new tags will be created from our class. Here's our new tag in action:

<highlight-element>Click me!</highlight-element>
Click me!

Note the required closing tag. Custom elements cannot be self-closing, the way that <img> or <path /> elements are. You must explicitly close your tags, even if there's nothing in them.

The class-based definition for custom elements gives us a familiar entryway as JavaScript developers, but there are some rules to keep in mind. Because these elements are meant to behave in the same way as the built-in elements, including being instantiated through document.createElement(), custom element constructors have some restrictions:

The browser may choose to automatically test your element to make sure it complies, and reject any clases that break the rules.

Of course, custom elements that couldn't touch their attributes or contents wouldn't be very useful. But if we can't access those parts of the component in the constructor, when can we do it? That's our next topic: the custom element lifecycle methods.