# custom element

[browser](/web/browser.md) ⟩ [web components](/web/component.md) ⟩ [implement](/web/component/implement.md) ⟩ custom elements

{% hint style="success" %}
associate a JavaScript [<mark style="color:yellow;">**class**</mark>](/web/js/val/class.md) with an [<mark style="color:yellow;">**HTML tag**</mark>](/web/html/element.md) name.
{% endhint %}

{% hint style="danger" %}
:star: custom element name 中間一定要有："<mark style="color:blue;">**`-`**</mark>" :exclamation:

* 例如：<mark style="color:blue;">`<vstack->`</mark> 或 <mark style="color:blue;">`<v-stack>`</mark> :white\_check\_mark:
* 但 "<mark style="color:blue;">`-`</mark>" 不能放前面：<mark style="color:blue;">`<-vstack>`</mark> :x:
  {% endhint %}

{% tabs %}
{% tab title="⭐️ 重點" %}
{% hint style="success" %}
define new custom elements with [customElements.define()](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements).
{% endhint %}

```javascript
// define new custom element
customElements.define(
  'web-component',                    // ⭐️ HTML tag name, must have "-"❗️ 
  class extends HTMLElement { ... }   // ⭐️ must extend `HTMLElement`❗️ 
);
```

{% hint style="warning" %}
**custom elements** render as <mark style="color:yellow;">**inline**</mark> elements <mark style="color:yellow;">**by default**</mark>.
{% endhint %}
{% endtab %}

{% tab title="🔴 主題" %}

* [lifecycle methods](/web/component/custom-element/lifecycle.md)
* [getter/setter for attributes](/web/component/custom-element/getter-setter.md) - make element's <mark style="color:yellow;">**attributes**</mark> available as <mark style="color:yellow;">**properties**</mark>.
* [Element Types](/web/component/custom-element/types.md)
* [Element Info](/web/component/custom-element/info.md)
* [Observing Attributes](/web/component/custom-element/observed-attributes.md)
* [Custom Element Upgrade](/web/component/examples/less-than-time-formatted-greater-than.md#custom-element-upgrade)
* [Element-defined Content](/web/component/custom-element/content.md)
  {% endtab %}

{% tab title="📘 手冊" %}

* Window ⟩ [.customElements](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements) ([CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry))
* [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) ⟩ [.define()](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define)
* ​​📘 [Using custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) ⭐️
* ​​📘 [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)
* Google ⟩ [Custom Elements](https://developers.google.com/web/fundamentals/web-components/customelements) ⭐️⭐️⭐️
* HTML Spec ⟩ [valid custom element name](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name)
  {% endtab %}

{% tab title="📗 參考" %}

* [ ] JavaScript: The Definitive Guide (15.6 Web Components)
* [ ] can I use [custom elements](https://caniuse.com/custom-elementsv1)?
  {% endtab %}

{% tab title="💈範例" %}
:floppy\_disk: replit: [\<inline-circle>](https://replit.com/@pegasusroe/lessinline-circlegreater#index.html)

{% embed url="<https://codepen.io/lochiwei/pen/wvmoxRP>" %}

📁 HTML

```html
    <p> 
        The document has one marble: 
        <inline-circle size="3em" color="red"></inline-circle>. <br>
            
        The HTML parser instantiates two more marbles:
        <inline-circle size="2em" color="blue"></inline-circle>
        <inline-circle color="green"></inline-circle>.

        <hr>
            
        How many marbles does the document contain now? 
    </p>
```

📁 JS

```javascript
// define custom element: 
customElements.define("inline-circle", class extends HTMLElement {

    // 🔸 when <inline-circle> inserted into document.
    connectedCallback() {        
        
        // styles for circles    
        // ⭐️ demo only: don't do this in real apps.    
        this.style.display = "inline-block";    
        this.style.borderRadius = "50%";     
        this.style.border = "solid black 1px";    
        this.style.transform = "translateY(10%)";         
        
        // default size    
        if (!this.size) this.size = "0.8em";
    }
    
    // 🔸 observed attributesstatic 
    get observedAttributes() { return ["size", "color"]; }
    
    // 🔸 when one of the attributes listed above changes.
    attributeChangedCallback(name, oldValue, newValue) {    
        switch (name) {                    
            case "size":            
                this.style.width = newValue;             
                this.style.height = newValue;             
                break;        
            case "color":            
                this.style.backgroundColor = newValue;             
                break;    
        }
    }
    
    // 🔸 getter/setter for element's attributes.
    get size() { return this.getAttribute("size"); } 
    set size(size) { this.setAttribute("size", size); } 
    get color() { return this.getAttribute("color"); } 
    set color(color) { this.setAttribute("color", color); }
});
```

{% endtab %}
{% endtabs %}

## Valid Custom Element Name

{% hint style="info" %}

* 開頭：\[a-z] ⭐️
* 接著：\[0-9], \[a-z], "**`-`**", "**`.`**", "**`_`**" (optional)
* 中間一定要有："**`-`**" ⭐️
* 接著：\[0-9], \[a-z], "**`-`**", "**`.`**", "**`_`**" (optional)
  {% endhint %}

{% hint style="info" %}
\<vstack-> 或 \<i-vstack> ❓(這兩個都是合法的名字)
{% endhint %}

```javascript
PotentialCustomElementName ::= 
    [a-z] (PCENChar)* '-' (PCENChar)*
    
PCENChar ::=
    "-" | "." | [0-9] | "_" | [a-z] | #xB7 | 
    [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | 
    [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | 
    [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | 
    [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
```

## Register New Tag Name <a href="#register" id="register"></a>

We can create our tags by using the [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) object (`window.customElements`).

{% hint style="danger" %}

* **custom element names** must contain a **hyphen**:exclamation: \
  📘 customElements.[define](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#parameters)()
* Custom elements **cannot** be **self-closing** because HTML only allows [a few elements](https://html.spec.whatwg.org/multipage/syntax.html#void-elements) to be self-closing. Always write a closing tag\
  &#x20;(`<custom-element></custom-element>`)\
  📘 Google ⟩ [Custom Elements](https://developers.google.com/web/fundamentals/web-components/customelements#jsapi)
  {% endhint %}

```javascript
// ⭐️ custom tag <my-component>
// ⭐️ custom element names must contain a "hyphen".
customElements.define('my-component', MyComponent);
```

## Supporting Class <a href="#class" id="class"></a>

To create a custom element, we need to tell the browser several details about it: **how to show it**, **what to do** when the element is **added** or **removed** to page, etc.

```javascript
class MyComponent extends HTMLElement {

  constructor() {
    super();
    // ...
  }

  // triggered when added to the document
  connectedCallback() {}

  // triggered when removed from the document
  disconnectedCallback() {}

  // attribute names to monitor for changes
  static get observedAttributes() {
    return [ /* ... */ ];
  }

  // triggered when one of attributes listed above is modified.
  // doesn’t trigger for unlisted attributes (for performance reasons).
  attributeChangedCallback(name, oldValue, newValue) {}

  // called when moved to a new document
  // happens in document.adoptNode, very rarely used
  adoptedCallback() {}

  // other methods and properties ...
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/web/component/custom-element.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
