# implementing components

[browser](https://lochiwei.gitbook.io/web/browser) ⟩ [web components](https://lochiwei.gitbook.io/web/component) ⟩ implement

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

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

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

* [custom-element](https://lochiwei.gitbook.io/web/component/custom-element "mention") - associate a JS [class](https://lochiwei.gitbook.io/web/js/val/class "mention")with an [HTML tag](https://lochiwei.gitbook.io/web/html/element) name.
* [shadow-dom](https://lochiwei.gitbook.io/web/component/shadow-dom "mention") - encapsulation.
* [template](https://lochiwei.gitbook.io/web/component/template "mention") - \<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](https://lochiwei.gitbook.io/web/js/val/class)，這個類別繼承自 <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](https://lochiwei.gitbook.io/web/component/shadow-dom) 允許你為你的 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>
