Skip to main content Accessibility Feedback

Why not extend the button element?

In my video on HTML Web Components, I create a Web Component that makes a very generic “counter component.”

You click a button, and the button tracks how many times it’s been clicked.

customElements.define('count-up', class extends HTMLElement {

	constructor () {

		// Inherit parent class properties
		super();

		// Track the number of clicks
		this.count = 0;

		// Inject the HTML
		this.innerHTML = `<button>Clicked ${this.count} Times</button>`;

		// Listen for events on the button
		this.btn = this.querySelector('button');
		this.btn.addEventListener('click', this);

	}

	handleEvents () {
		this.count++;
		this.btn.textContent = `Clicked ${this.count} Times`;
	}

});

I got a great question in the comments asking why I extend my class with HTMLElement and not HTMLButtonElement.

This comes up a lot! There are two reasons:

  1. The Web Component itself isn’t a button. One of it’s child elements is. If I wanted the custom element itself to behave like a button, I’d also need to add stuff like a role attribute and additional event listeners.
  2. That’s also unfortunately not how Web Components work. You extend the HTMLElement, then layer in your custom behavior. You can’t reliably extend other element types with customElements.define().

There was a spec in the works that would let you extend native elements with custom functionality using is attribute. Firefox and Chromium moved forward with it, and then the WebKit team straight up refused to implement it, effectively killing the spec.

My understanding is that there’s a replacement spec in the works that WebKit is allegedly onboard with, but I don’t know much about it at this time as it’s nowhere near production ready.