Skip to content

Commit

Permalink
Fix issue 11225: Improve CustomElementRegistry.define() page
Browse files Browse the repository at this point in the history
  • Loading branch information
wbamberg committed Sep 26, 2023
1 parent 6d13165 commit 65b278b
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 203 deletions.
221 changes: 68 additions & 153 deletions files/en-us/web/api/customelementregistry/define/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,9 @@ page-type: web-api-instance-method
browser-compat: api.CustomElementRegistry.define
---

{{APIRef("CustomElementRegistry")}}
{{APIRef("Web Components")}}

The **`define()`** method of the
{{domxref("CustomElementRegistry")}} interface defines a new custom element.

There are two types of custom elements you can create:

- **Autonomous custom element**: Standalone elements; they don't inherit
from built-in HTML elements.
- **Customized built-in element**: These elements inherit from — and
extend — built-in HTML elements.
The **`define()`** method of the {{domxref("CustomElementRegistry")}} interface adds a definition for a custom element to the custom element registry, mapping its name to the constructor which will be used to create it.

## Syntax

Expand All @@ -28,8 +20,7 @@ define(name, constructor, options)
### Parameters

- `name`
- : Name for the new custom element. Note that custom element names must contain a
hyphen.
- : Name for the new custom element. Must be a [valid custom element name](#valid_custom_element_names).
- `constructor`
- : Constructor for the new custom element.
- `options` {{optional_inline}}
Expand All @@ -38,7 +29,7 @@ define(name, constructor, options)

- `extends`
- : String specifying the name of a built-in element to
extend. Used to create a _customized built-in element_.
extend. Used to create a customized built-in element.

### Return value

Expand All @@ -47,180 +38,100 @@ None ({{jsxref("undefined")}}).
### Exceptions

- `NotSupportedError` {{domxref("DOMException")}}
- : Thrown if the {{domxref("CustomElementRegistry")}} already contains an
entry with the same name or the same constructor (or is otherwise
already defined), or <code>extends</code> is specified and it is a
[valid custom element name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name),
or <code>extends</code> is specified but the element it is trying to extend is an unknown element.
- : Thrown if:
- The {{domxref("CustomElementRegistry")}} already contains an entry with the same name or the same constructor (or is otherwise already defined).
- The <code>extends</code> option is specified and it is a [valid custom element name](#valid_custom_element_names)
- The <code>extends</code> option is specified but the element it is trying to extend is an unknown element.
- `SyntaxError` {{domxref("DOMException")}}
- : Thrown if the provided name is not a [valid custom element name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name).
- : Thrown if the provided name is not a [valid custom element name](#valid_custom_element_names).
- {{jsxref("TypeError")}}
- : Thrown if the referenced constructor is not a constructor.

> **Note:** You'll often get `NotSupportedError`s thrown that
> seem like `define()` is failing, but instead it is likely a problem with
> {{domxref("Element.attachShadow()")}}.
## Description

## Examples
The `define()` method adds a definition for a custom element to the custom element registry, mapping its name to the constructor which will be used to create it.

### Autonomous custom element
There are two types of custom element you can create:

The following code is taken from our [popup-info-box-web-component](https://github.com/mdn/web-components-examples/tree/main/popup-info-box-web-component)
example ([see it live also](https://mdn.github.io/web-components-examples/popup-info-box-web-component/)).
- _Autonomous custom element_ are standalone elements, that don't inherit from built-in HTML elements.
- _Customized built-in element_ are elements that inherit from, and extend, built-in HTML elements.

```js
// Create a class for the element
class PopUpInfo extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
To define an autonomous custom element, you should omit the `options` parameter.

// Create a shadow root
const shadow = this.attachShadow({ mode: "open" });

// Create spans
const wrapper = document.createElement("span");
wrapper.setAttribute("class", "wrapper");

const icon = document.createElement("span");
icon.setAttribute("class", "icon");
icon.setAttribute("tabindex", 0);

const info = document.createElement("span");
info.setAttribute("class", "info");

// Take attribute content and put it inside the info span
const text = this.getAttribute("data-text");
info.textContent = text;

// Insert icon
const img = document.createElement("img");
img.src = this.hasAttribute("img")
? this.getAttribute("img")
: "img/default.png";
icon.appendChild(img);

// Create some CSS to apply to the shadow dom
const style = document.createElement("style");
console.log(style.isConnected);

style.textContent = `
.wrapper {
position: relative;
}
.info {
font-size: 0.8rem;
width: 200px;
display: inline-block;
border: 1px solid black;
padding: 10px;
background: white;
border-radius: 10px;
opacity: 0;
transition: 0.6s all;
position: absolute;
bottom: 20px;
left: 10px;
z-index: 3;
}
img {
width: 1.2rem;
}
.icon:hover + .info, .icon:focus + .info {
opacity: 1;
}
`;

// Attach the created elements to the shadow dom
shadow.appendChild(style);
console.log(style.isConnected);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);
}
}
To define a customized built-in element, you must pass the `options` parameter with its `extends` property set to the name of the built-in element that you are extending, and this must correspond to the interface that your custom element class definition inherits. For example, to customize the {{htmlelement("p")}} element, you must pass `{extends: "p"}` to `define()`, and the class definition for your element must inherit from {{domxref("HTMLParagraphElement")}}.

// Define the new element
customElements.define("popup-info", PopUpInfo);
```
### Valid custom element names

```html
<popup-info
img="img/alt.png"
data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></popup-info>
```
Custom element names must:

- start with an ASCII lower-case letter (a-z)
- contain a hypen
- not contain any ASCII upper-case letters
- not contain certain other characters, as documented in the [valid custom element names](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) section of the Web Components specification
- not be any of:
- "annotation-xml"
- "color-profile"
- "font-face"
- "font-face-src"
- "font-face-uri"
- "font-face-format"
- "font-face-name"
- "missing-glyph"

> **Note:** Constructors for autonomous custom elements must extend
> {{domxref("HTMLElement")}}.
## Examples

### Customized built-in element
### Defining an autonomous custom element

The following code is taken from our [word-count-web-component](https://github.com/mdn/web-components-examples/tree/main/word-count-web-component)
example ([see it live also](https://mdn.github.io/web-components-examples/word-count-web-component/)).
The following class implements a minimal autonomous custom element:

```js
// Create a class for the element
class WordCount extends HTMLParagraphElement {
class MyAutonomousElement extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
}
}
```

// count words in element's parent element
const wcParent = this.parentNode;
This element doesn't do anything: a real autonomous element would implement its functionality in its constructor and in the lifecycle callbacks provided by the standard. See [Implementing a custom element](/en-US/docs/Web/API/Web_components/Using_custom_elements) in our guide to working with custom elements.

function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
However, the above class definition satisfies the requirements of the `define()` method, so we can define it with the following code:

const count = `Words: ${countWords(wcParent)}`;
```js
customElements.define("my-autonomous-element", MyAutonomousElement);
```

// Create a shadow root
const shadow = this.attachShadow({ mode: "open" });
We could then use it in an HTML page like this:

// Create text node and add word count to it
const text = document.createElement("span");
text.textContent = count;
```html
<my-autonomous-element>Element contents</my-autonomous-element>
```

### Defining a customized built-in element

// Append it to the shadow root
shadow.appendChild(text);
The following class implements a customized built-in element:

// Update count when element content changes
setInterval(() => {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
```js
class MyCustomizedBuiltInElement extends HTMLParagraphElement {
constructor() {
super();
}
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });
```

```html
<p is="word-count"></p>
```

### Creating an element which disables the ability to attach a shadow root
This element extends the built-in {{htmlelement("p")}} element.

If the class used for the element contains the static property `disabledFeatures` returning the string \`shadow\` this will cause {{domxref("Element.attachShadow()")}} to return a `NotSupportedError` {{domxref("DOMException")}}.
In this minimal example the element doesn't implement any customization, so it will behave just like a normal `<p>` element. However, it does satisfy the requirements of `define()`, so we can define it like this:

```js
class PopUpInfo extends HTMLElement {
static get disabledFeatures() {
return ["shadow"];
}
customElements.define("my-customized-element", MyCustomizedElement, {
extends: "p",
});
```

constructor() {
super();
We could then use it in an HTML page like this:

const shadow = this.attachShadow({ mode: "open" });
// this will cause an error to be thrown when the element is defined.
}
}
```html
<p is="my-customized-element"></p>
```

## Specifications
Expand All @@ -230,3 +141,7 @@ class PopUpInfo extends HTMLElement {
## Browser compatibility

{{Compat}}

## See also

- [Using custom elements](/en-US/docs/Web/API/Web_components/Using_custom_elements)
2 changes: 1 addition & 1 deletion files/en-us/web/api/customelementregistry/get/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ page-type: web-api-instance-method
browser-compat: api.CustomElementRegistry.get
---

{{APIRef("CustomElementRegistry")}}
{{APIRef("Web Components")}}

The **`get()`** method of the
{{domxref("CustomElementRegistry")}} interface returns the constructor for a
Expand Down
2 changes: 1 addition & 1 deletion files/en-us/web/api/customelementregistry/getname/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ page-type: web-api-instance-method
browser-compat: api.CustomElementRegistry.getName
---

{{APIRef("CustomElementRegistry")}}
{{APIRef("Web Components")}}

The **`getName()`** method of the
{{domxref("CustomElementRegistry")}} interface returns the name for a
Expand Down
48 changes: 2 additions & 46 deletions files/en-us/web/api/customelementregistry/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ page-type: web-api-interface
browser-compat: api.CustomElementRegistry
---

{{DefaultAPISidebar("Web Components")}}
{{APIRef("Web Components")}}

The **`CustomElementRegistry`** interface provides methods for registering custom elements and querying registered elements. To get an instance of it, use the {{domxref("window.customElements")}} property.

Expand All @@ -24,51 +24,7 @@ The **`CustomElementRegistry`** interface provides methods for registering custo

## Examples

The following code is taken from our [word-count-web-component](https://github.com/mdn/web-components-examples/tree/main/word-count-web-component) example ([see it live also](https://mdn.github.io/web-components-examples/word-count-web-component/)). Note how we use the {{domxref("CustomElementRegistry.define()")}} method to define the custom element after creating its class.

```js
// Create a class for the element
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();

// count words in element's parent element
const wcParent = this.parentNode;

function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}

const count = `Words: ${countWords(wcParent)}`;

// Create a shadow root
const shadow = this.attachShadow({ mode: "open" });

// Create text node and add word count to it
const text = document.createElement("span");
text.textContent = count;

// Append it to the shadow root
shadow.appendChild(text);

// Update count when element content changes
setInterval(() => {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });
```

> **Note:** The `CustomElementRegistry` is available through the {{domxref("Window.customElements")}} property.
See the [Examples](/en-US/docs/Web/API/Web_components/Using_custom_elements#examples) section in our [guide to using custom elements](/en-US/docs/Web/API/Web_components/Using_custom_elements).

## Specifications

Expand Down
2 changes: 1 addition & 1 deletion files/en-us/web/api/customelementregistry/upgrade/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ page-type: web-api-instance-method
browser-compat: api.CustomElementRegistry.upgrade
---

{{APIRef("CustomElementRegistry")}}
{{APIRef("Web Components")}}

The **`upgrade()`** method of the
{{domxref("CustomElementRegistry")}} interface upgrades all shadow-containing custom
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ page-type: web-api-instance-method
browser-compat: api.CustomElementRegistry.whenDefined
---

{{APIRef("CustomElementRegistry")}}
{{APIRef("Web Components")}}

The **`whenDefined()`** method of the
{{domxref("CustomElementRegistry")}} interface returns a {{jsxref("Promise")}} that
Expand Down

0 comments on commit 65b278b

Please sign in to comment.