Web Componentsの作り方
Web Componentsは次の3つの技術を組み合わせることで作れます。👇
- Custom Elements → タグを作る
- Shadow DOM → 見た目を守る
- HTML Templates → 中身を使い回す
👉 この3つを理解すれば、実用的なコンポーネントが作れるようになります。
Custom Elements(カスタムタグ)
役割
👉 独自のHTMLタグを作る
最小サンプル
まずは「タグを作るだけ」
class MyGreeting extends HTMLElement {
connectedCallback() {
this.innerHTML = "<p>こんにちは!</p>";
}
}
customElements.define("my-greeting", MyGreeting);
HTMLで使う:
<my-greeting></my-greeting>
ポイント
- クラスは HTMLElement を継承
- customElements.define() で登録
- タグ名には「-(ハイフン)」が必須
👉 例:my-button, user-card
独自サンプル:属性付きコンポーネント
class UserName extends HTMLElement {
connectedCallback() {
const name = this.getAttribute("name") || "ゲスト";
this.innerHTML = `<p>ようこそ、${name}さん!</p>`;
}
}
customElements.define("user-name", UserName);
<user-name name="山田"></user-name>
👉 HTMLだけで動的なUIが作れる
Shadow DOM(カプセル化)
役割
👉 CSSやHTMLを外部から隔離する。
なぜ必要?
普通のHTMLだと…
p {
color: red;
}
👉 全部の <p> に影響してしまう。
Shadow DOMを使うことで、Web Components内部だけにCSSの影響を限定できます。
class MyBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
p {
color: blue;
}
</style>
<p>この文字は青だけど外には影響しない</p>
`;
}
}
customElements.define("my-box", MyBox);
ポイント
- attachShadow() で有効化
- shadowRoot にHTMLを書く
- 外部CSSの影響を受けない&与えない
👉 CSS地獄を防ぐ最重要機能
独自サンプル:ボタンコンポーネント
class MyButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
button {
background: black;
color: white;
padding: 10px;
border-radius: 5px;
}
</style>
<button>クリック</button>
`;
}
}
customElements.define("my-button", MyButton);
👉 ページのCSSに影響されない安全なボタン
HTML Templates(テンプレート)
役割
👉 HTML構造を使い回す。
なぜ必要?
毎回こう書いたり、
this.shadowRoot.innerHTML = `...長いHTML...`;
こう書くのは面倒:
this.table = document.createElement("table");
this.thead = document.createElement("thead");
this.tbody = document.createElement("tbody");
this.tfoot = document.createElement("tfoot");
this.table.appendChild(this.thead);
this.table.appendChild(this.tbody);
this.table.appendChild(this.tfoot);
👉 HTMLをテンプレートとして分離すると使い回しもできて、見やすくなる。
基本の使い方
<template id="card-template">
<style>
.card {
border: 1px solid #ccc;
padding: 10px;
}
</style>
<div class="card">
<slot></slot>
</div>
</template>
JavaScript側
class MyCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
const template = document.getElementById("card-template");
const content = template.content.cloneNode(true);
this.shadowRoot.appendChild(content);
}
}
customElements.define("my-card", MyCard);
使用例
<my-card>
<p>カードの中身です</p>
</my-card>
ポイント
<template>は画面に表示されない- cloneNode(true) でコピーして使う
<slot>で中身を差し込める
👉 実務ではほぼ必須レベル
3つを組み合わせた完成形(実践サンプル)
「プロフィールカード」コンポーネント
HTML
<template id="profile-template">
<style>
.card {
border: 1px solid #ddd;
padding: 10px;
border-radius: 8px;
}
.name {
font-weight: bold;
}
</style>
<div class="card">
<p class="name"></p>
<slot></slot>
</div>
</template>
<profile-card name="山田太郎">
<p>エンジニアです</p>
</profile-card>
JavaScript
class ProfileCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
const template = document.getElementById("profile-template");
const content = template.content.cloneNode(true);
const name = this.getAttribute("name") || "不明";
content.querySelector(".name").textContent = name;
this.shadowRoot.appendChild(content);
}
}
customElements.define("profile-card", ProfileCard);
ここが重要
この1つのコンポーネントに👇が全部入っています
- Custom Elements →
<profile-card> - Shadow DOM → スタイル隔離
- Template → 再利用可能構造
👉 これがWeb Componentsの完成形です
実務での考え方(かなり重要)
よくある設計パターン
- 良い設計
- UI単位で分割
- 属性でデータ受け渡し
- slotで柔軟性確保
悪い設計
- 1コンポーネントが巨大
- JS依存が強すぎる
- slotを使わない
まとめ
Web Componentsの作り方はシンプルです👇
- Custom Elements → タグを作る
- Shadow DOM → 安全にする
- Template → 使い回す
👉 この3つを組み合わせるだけ