Defining new elements for your HTML document

Browsers come with a set of built-in elements like <p> or <input>. When we need a new component not covered by that, we often build it from <div> and <span> tags. An alternative is to introduce a new element, like <my-element>.

When a browser encounters an unknown element like <my-element>, the browser will proceed to render <my-element>'s children. The visual rendering of your page will not be affected.

If you care about their HTML being valid, your new element should contain a dash character (-) to mark it as a custom element. Custom elements will be ignored for the purposes HTML validation, but their children will still be validated.

Styling new elements

Browsers will give unknown elements a default style of display: inline. This way the element will not affect the layout flow Show archive.org snapshot .

You may style new elements with CSS:

my-element {
  display: block;
  font-weight: bold;
}

If you have been in web development for a long time, you might remember browsers not applying styles to unknown elements. This was fixed in Internet Explorer 9, and hasn't been an issue for a decade.

If your new element isolates its children in a shadow DOM Show archive.org snapshot , they do not inherit styles from the document. See Styling a Web Component Show archive.org snapshot for this case.

Applying JavaScript behavior to new elements

All client-side JavaScript frameworks comes with mechanisms to activate JavaScript behavior on certain DOM elements. E.g. in Unpoly Show archive.org snapshot uses compilers Show archive.org snapshot for that purpose. Instead of using a framework mechanism you may use customElements.define() Show archive.org snapshot to register your custom element's JavaScript behavior with the browser directly.

A big advantage of using the browser's customElements.define() is that your new element does not not need to be compiled or activated after insertion. Attaching a <my-element> to the document will automatically activate the registered behavior. This decouples your custom element from a particular JavaScript framework.

Here is an example:

class MyElement extends HTMLElement {
  connectedCallback() {
    // the element was added to the DOM
  }

  disconnectedCallback() {
    // the element was removed from the DOM
  }

  attributeChangedCallback(attribute, oldValue, newValue) {
    // the given attribute was changed
  }
  
  foo() {
    // a custom method  
  }
}

customElements.define('my-element', MyElement)

Note that this class automatically describes the JavaScript API for your new element. The DOM node objects are their own JavaScript representation:

let newElement = new MyElement()
document.body.appendChild(newElement)

let queriedElement = document.querySelector('my-element')
console.log(newElement === queriedElement) // true

queriedElement.foo()

Transpiling custom elements

Note that custom element classes must not be transpiled down to ES5 prototypes. You must send native ES6 classes to the browser. All browsers except IE11 have been supporting ES6 classes natively for many years, so you might want to stop dumbing down your ES6 classes anyway.

Henning Koch About 4 years ago