カスタム要素を使ってみる(自律カスタム要素)(Web Components)

#概要
紹介ページのSampleに従って、カスタム要素を使ってみる。

今回は、画像詳細を表示する自律カスタム要素`<popup-info>`。
#内容
##環境
macOS Catalina
Firefox 86.0
##全体像
```shell
$ tree .
.
├── img
│   ├── alt.png
│   └── default.png
├── main.js
└── popup_info.html
```
```html:popup_info.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Pop-up info box — web components</title>
    <script src="main.js" defer></script>
  </head>
  <body>
    <h1>Pop-up info widget - web components</h1>

    <form>
      <div>
        <label for="cvc">Enter your CVC <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></label>
        <input type="text" id="cvc">
      </div>
    </form>
  </body>
</html>
```
```javascript:main.js
// Create a class for the element
class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    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');
    info.textContent = this.getAttribute('data-text');

    // Insert icon
    let imgUrl;
    if(this.hasAttribute('img')) {
      imgUrl = this.getAttribute('img');
    } else {
      imgUrl = 'img/default.png';
    }

    const img = document.createElement('img');
    img.src = imgUrl;
    icon.appendChild(img);

    // Create some CSS to apply to the shadow dom
    const style = document.createElement('style');
    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
    const shadow = this.attachShadow({mode: 'open'});
    shadow.appendChild(style);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}

// Define the new element
customElements.define('popup-info', PopUpInfo);
```
(少しソースは個人的に見やすく変更した。)
([公式デモページ](https://mdn.github.io/web-components-examples/popup-info-box-web-component/))
写真アイコンの上にマウスを持ってくると、説明がpopupしてくる。

|![スクリーンショット 2021-03-08 18.47.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166238/b548127c-c700-b595-5307-c985f9176c79.png)|
|:-:|

##前提として2種類
前提としてカスタム要素には、2種類ある。
- **自律カスタム要素** ：標準のHTML要素を継承しない。例：`<popup-info>`
- カスタマイズされた組み込み要素 ：標準のHTML要素を継承する。例：`<p is="word-count">`(`is`属性を使う)
##カスタム要素を記載する
```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>
```
##カスタム要素を定義する
`CustomElementRegistry.define()`を用いて、カスタム要素を登録する。

```javascript
customElements.define('popup-info', PopUpInfo);
```
- `'popup-info'`: カスタム要素の名前。1つ以上の`-`が必要。
- `PopUpInfo`: カスタム要素の振る舞いを定めるclass。

##カスタム要素のクラスを定義する。(`PopUpInfo`)
`HTMLElement`を拡張して、`PopUpInfo`を定義する。

```javascript
class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // write element functionality in here
    ...
  }
}
```
#参考にさせていただいた本・頁
- https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
- https://developer.mozilla.org/ja/docs/Web/Web_Components/Using_custom_elements

#感想
ソースで見かける見知らぬタグの使い方が分かって良かった。
#今後
shadow要素については理解が曖昧なので、いつかまとめたい。