Promising definition

CSS gives you the option to hide custom elements that are yet to be defined, yet to be associated with a proper constructor. This is possible with the :defined pseudo class.

quick-review:not(:defined) {
	display: none;
}

Thankfully, it is also possible for all types of custom elements given the right selector.

[is='toggle-button']:not(:defined) {
	display: none;
}

In both instances you remove the element from the flow of the document and from the accessibility tree, so that the page is not burdened for nothing. To see the element, then, you have to run a script, to create a custom element and pair the class with the ad-hoc name.

But in a script, there is a JavaScript alternative to the CSS way. Instead of hiding the node in the DOM, as long as it is undefined, you can add the element only after the definition has gone through.

To do this the customElements interface offers the whenDefined method.

customElements.whenDefined('time-stamp');

The function returns a promise with a rather peculiar behavior. If the custom element is already defined, it will resolve immediately. If not, it will patiently wait for the momentous occasion.

Either way, given the promise, you can chain the .then function for the happy path. Here you have access to the class constructor, and the opportunity to create the element.

customElements.whenDefined('time-stamp').then((constructor) => {
	// ...
});

document.createElement is then one of the most clear API to complete the task. The function requires only the name of the element, and you don’t even need to repeat the hard-coded string.

const node = document.createElement('time-stamp');

The customElements module comes back to help you with another function: getName. Given the class fabricating the custom element — what luck, the value returned by the promise —, the function returns the matching tag name.

const name = customElements.getName(constructor);

So that you can use the name directly and append the node where it might fit the application best.

const node = document.createElement(name);
document.body.appendChild(node);

Since whenDefined returns a promise, you are not wrong in anticipating a possible catch.

customElements
	.whenDefined('time-stamp')
	.then((constructor) => {
		// ...create & append
	})
	.catch((error) => {
		// ...
	});

You can indeed anticipate, and respond to, a mistake, but there’s only one instance when the promise will be rejected, only when the name used for the element is invalid.

Console DOMException An invalid or illegal string was specified

For a custom element to be valid you need at least two words separated with a hyphen, to distinguish the structure from standard HTML elements. Outside of this requirement there are a few more restrictions, but as long as you don’t tempt the browser with eccentric characters, you will be safe with whatever nick-name.

I prefer to work from the ground up, in increments and enhancements. Start with HTML, with solid markup foundations. Complement the structure with CSS. Finally consider JavaScript, but similar to a bonus; one more opportunity to solve specific issues and manage complex interactions. But there is certainly a use for the whenDefined function in place of, or on top of, the :defined path. And one more way to celebrate the instant custom elements are officially defined.