# implementing components

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

{% tabs %}
{% tab title="💈範例" %}

* [web component (0.0.1)(Gemini)](https://codepen.io/pegasusroe/pen/NPKgGZg)：初步教學範例
  {% endtab %}

{% tab title="👥 相關" %}

* [custom element](/web/component/custom-element.md) - associate a JS [class](/web/js/val/class.md)with an [HTML tag](/web/html/element.md) name.
* [shadow DOM](/web/component/shadow-dom.md) - encapsulation.
* [\<template>](/web/component/template.md) - \<template>
  {% endtab %}

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

* [ ] JavaScript: The Definitive Guide (15.6 Web Components)
  {% endtab %}
  {% endtabs %}

## 1️⃣ **定義**繼承自 HTMLElement 的 **class** <a href="#define" id="define"></a>

首先，你需要定義一個 JavaScript [class](/web/js/val/class.md)，這個類別繼承自 <mark style="color:blue;">HTMLElement</mark>。這個類別將定義你的 Web Component 的行為。

```javascript
// ⭐️ 1️⃣ 定義一個繼承自 HTMLElement 的 class
class MyComponent extends HTMLElement {
  constructor() {
  
    // ⭐️ 呼叫父類別的 constructor
    super(); 

    // 在這裡初始化你的 component
  }
}
```

## 2️⃣ **建立 Shadow DOM** <a href="#shadow-dom" id="shadow-dom"></a>

[Shadow DOM](/web/component/shadow-dom.md) 允許你為你的 Web Component 創建一個封裝的 DOM 樹。這意味著 component 內部的樣式和腳本不會影響到頁面的其他部分，反之亦然。

```javascript
constructor() {

  super();

  // ⭐️ 2️⃣ 建立 shadow DOM
  //   mode: 
  //   - 'open'  : 允許 JS 從外部訪問 shadow DOM
  //   - 'closed': 不允許
  this.attachShadow({ mode: 'open' }); 
}
```

## 3️⃣ 建立樣板（Template） <a href="#template" id="template"></a>

你可以使用 <mark style="color:blue;">`<template>`</mark> 元素來定義你的 Web Component 的 HTML 結構。這樣做的好處是可以避免直接操作 DOM，提高效能。

```html
<!-- ⭐️ 3️⃣ 建立樣板 -->
<template id="my-component-template">

  <style>
    /* component 的樣式 */
    .container {
      border: 1px solid black;
      padding: 10px;
    }
  </style>
  
  <div class="container">
    <h1>Hello from My Component!</h1>
    <p>
      <!-- <slot> 是一個 placeholder，允許使用者在 component 標籤內插入內容 -->
      <slot></slot>
    </p>  
  </div>
  
</template>
```

## 4️⃣ 將樣板加入 Shadow DOM <a href="#append-template-content" id="append-template-content"></a>

在你的 component 類別中，使用 JavaScript 將樣板的內容複製到 Shadow DOM 中：

```javascript
constructor() {
  super();
  this.attachShadow({ mode: 'open' });

  // ⭐️ 4️⃣ append template content to shadow root
  const template = document.getElementById('my-component-template');
  this.shadowRoot.appendChild(template.content.cloneNode(true));
}
```

或者也可以直接將 3️⃣ 4️⃣ 兩個步驟濃縮成這樣：

```javascript
// in constructor()
// ⭐️ 4️⃣ append template content to shadow root
this.shadowRoot.innerHTML = `
    <style> ... </style>
    <div class="container"> ... </div>
`;
```

## 5️⃣ 註冊 Component <a href="#register" id="register"></a>

使用 <mark style="color:blue;">`customElements.define()`</mark> 註冊 Web Component：

* :star: 注意：component 的標籤名稱<mark style="color:red;">必須</mark><mark style="color:yellow;">包含一個連字號</mark> "<mark style="color:blue;">`-`</mark>":exclamation:

<pre class="language-javascript"><code class="lang-javascript"><strong>// ⭐️ 5️⃣ 註冊 Component       ╭──tag name──╮  ╭─ class ─╮
</strong><strong>window.customElements.define('my-component', MyComponent);
</strong></code></pre>


---

# 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/implement.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.
