Element: attachShadow() メソッド
Baseline
広く利用可能
*
この機能は広く実装されており、多くのバージョンの端末やブラウザーで動作します。2020年1月以降、すべてのブラウザーで利用可能です。
* この機能の一部は、対応レベルが異なる場合があります。
attachShadow() は Element インターフェイスのメソッドで、シャドウ DOM ツリーを特定の要素に設定し、そのシャドウルート (ShadowRoot) への参照を返します。
構文
attachShadow(options)
引数
options-
以下のフィールドを含むオブジェクトです。
mode-
文字列で、シャドウ DOM ツリーのカプセル化モードを指定します。 以下のいずれかの値を取ります。
open-
シャドウルート内の要素には、JavaScript から
shadowRootプロパティを介してアクセスすることができます。 closed-
シャドウルート内の要素には、JavaScript から
shadowRootプロパティを介してアクセスすることはできません。このプロパティはnullに設定されています。
clonable省略可-
論理値で、シャドウルートが複製可能かどうかを指定します。
trueに設定すると、Node.cloneNode()またはDocument.importNode()を使用してシャドウホストを複製した際、コピーにシャドウルートが含まれます。デフォルト値はfalseです。 customElementRegistry省略可-
接続されたシャドウルートの スコープ付きカスタム要素レジストリー として使用される
CustomElementRegistryです。nullまたはundefinedの場合、シャドウルートはWindow.customElementsで参照されるグローバルレジストリーを使用します。 delegatesFocus省略可-
論理値で、
trueに設定された場合、フォーカス可能性に関するカスタム要素の問題を緩和します。シャドウ DOM のフォーカスができない部分がクリックされた場合、最初のフォーカス可能な部分がフォーカスを得て、シャドウホストは:focusのスタイルを利用することができます。デフォルト値はfalseです。 referenceTarget省略可-
ホスト要素の外部からシャドウホストに対して行われる要素参照の有効なターゲットを示す文字列値。この値は、シャドウ DOM 内の要素の ID である必要があります。この値が設定されている場合、シャドウ DOM の外部からホスト要素への参照が行われると、参照先の対象要素が、そのホスト要素への参照の有効なターゲットとなります。
serializable省略可-
論理値で、
trueに設定されると、シャドウルートがシリアライズ可能であることを示します。 この設定が有効になっている場合、Element.getHTML()またはShadowRoot.getHTML()メソッドを、options.serializableShadowRoots引数をtrueに設定して呼び出すことで、シャドウルートをシリアライズすることができます。 デフォルト値はfalseです。 slotAssignment省略可-
シャドウ DOM ツリーの スロット割り当てモード を指定します。これは以下のどちらかです。
named-
要素は自動的にこのシャドウルート内の
<slot>要素に割り当てられます。 このシャドウルート内の<slot>のname属性と一致するslot属性を持つホスティングの子孫は、そのスロットに割り当てられます。 ホスティングの最上位の子でslot属性を持たないものは、name属性を持たない<slot>(「デフォルトのスロット」)が存在する場合、そのスロットに割り当てられます。 manual-
要素は、
HTMLSlotElement.assign()を使用して、特定のスロット要素に手動で割り当てられます。 自動的な割り当ては行われません。
返値
ShadowRoot オブジェクトです。
例外
NotSupportedErrorDOMException-
要素にシャドウルートを設定しようとすると、次のエラーが発生する場合があります。
- HTML 名前空間の外にある要素、またはシャドウを設定することができない要素であった場合。
- 要素定義の静的プロパティ
disabledFeaturesに"shadow"の値が指定された場合。 - 宣言的に作成されていないシャドウルートをすでに持っている要素。
- 宣言的なシャドウルートを持っているが、指定された
modeが既存のモードと一致しない場合。 nullまたはローカルスコープのレジストリー(new CustomElementRegistry()を使用して作成したもの)以外のcustomElementRegistry値を渡した場合。 グローバルレジストリーを渡した場合、このエラーが発生します。
解説
Element.attachShadow() メソッドは、指定された要素にシャドウ DOM ツリーを添付し、その ShadowRoot への参照を返します。
これは、ShadowRoot を生成するためのプログラム的な仕組みです。これは、ホスト要素に紐付けられたシャドウ DOM のルートノードです(<template> 要素の shadowrootmode 属性を使用して、宣言的に ShadowRoot を生成することもできます)。
これはカスタム要素を作成するために使用します。
シャドウツリーを追加できる要素
シャドウルートはすべての要素に追加できるわけではありません。セキュリティ上の理由でシャドウ DOM を持てないものもあります(<a> など)。
以下にシャドウルートを追加できる要素を列挙します。
すでにシャドウホストとなっている要素に対するこのメソッドの呼び出し
このメソッドは、指定されたモード mode が既存のモードと一致する場合に限り、すでに宣言型シャドウルートを持つ要素に対して呼び出スことができます。
この場合、すでに存在していた ShadowRoot はクリアされて返されます。
これにより、例えば、サーバーサイドレンダリングによってシャドウルートが宣言的に生成された後、クライアントサイドのコードがそのルートを再度設定しようとするようなケースに対応することができます。
そうでない場合、すでにシャドウルートを持つ要素に対して attachShadow() を呼び出すと、例外が発生します。
open および closed のシャドウルート
シャドウルートには、open または close のいずれかで指定されるカプセル化モードを設定することができます。
{mode: "open"} 引数が渡された場合、ホスト要素の shadowRoot プロパティを使用して、関連付けられたシャドウルートを取得することができます。
これにより、シャドウ DOM 内の要素にアクセスすることができます。
element.attachShadow({ mode: "open" });
element.shadowRoot; // ShadowRoot オブジェクトを返す
{mode: "closed"} が渡された場合、その Element の shadowRoot プロパティは null に設定されます。
なお、関数が返す値を保存しておけば、JavaScript から閉じられたシャドウルートにアクセスすることは可能です。
element.attachShadow({ mode: "closed" });
element.shadowRoot; // null を返す
例
>文字数カウントのカスタム要素
以下の例は word-count-web-component のデモを使用しています(実行例)。
コードの途中で attachShadow() を使ってシャドウルートを作成し、そこにカスタム要素の中身を設定しているのがわかると思います。
// その要素用のクラスを作成
class WordCount extends HTMLParagraphElement {
constructor() {
// コンストラクターでは、常にまず super を呼び出す
super();
// 要素の親要素に含まれる単語数を数える
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
// シャドウルートを作成
const shadow = this.attachShadow({ mode: "open" });
// テキストノードを作成し、そこに単語数を追加
const text = document.createElement("span");
text.textContent = count;
// シャドウルートに追加
shadow.appendChild(text);
// 要素のコンテンツが変更された際に更新回数をカウント
this.parentNode.addEventListener("input", () => {
text.textContent = `Words: ${countWords(wcParent)}`;
});
}
}
// 新しい要素を定義
customElements.define("word-count", WordCount, { extends: "p" });
シャドウ DOM の無効化
要素に disabledFeatures という名前付きの静的プロパティがあり、その値が文字列 "shadow" を含む配列である場合、attachShadow() の呼び出しにより例外が発生します。
例を示します。
class MyCustomElement extends HTMLElement {
// この要素のシャドウ DOM を無効化
static disabledFeatures = ["shadow"];
constructor() {
super();
}
connectedCallback() {
// シャドウルートを作成
// これは例外が発生する
const shadow = this.attachShadow({ mode: "open" });
}
}
// 新しい要素を定義
customElements.define("my-custom-element", MyCustomElement);
名前付きスロットの割り当て
この例では、名前付きスロットの割り当てについて説明しています。
ウェブコンポーネントの作成
このコードは、記事のタイトル、メタデータ、本文セクション用の 3 つの名前付きスロットを持つウェブ要素を作成します。
ShadowRoot は、カスタム要素のコンストラクター内で添付されます。
slotAssignment: "named" オプションはデフォルトで設定されているため、明示的に設定する必要はありません。
class MyArticle extends HTMLElement {
constructor() {
super();
// シャドウルートを設定
this.attachShadow({ mode: "open" /* , slotAssignment: "named" */ });
}
connectedCallback() {
this.render();
}
render() {
// 内部構造とスタイルを定義
this.shadowRoot.innerHTML = `
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot name="title"></slot>
</h2>
<div class="meta">
<slot name="meta"></slot>
</div>
<div class="body">
<slot></slot>
</div>
`;
}
}
// コンポーネントを登録
customElements.define("my-article", MyArticle);
ウェブコンポーネントの使用
下記 HTML では、先ほど作成した <my-article> ウェブコンポーネントを使用しています。
ネストされた要素は、名前の照合に基づいてコンポーネントのスロット内にレンダリングされます。
名前が指定されていない要素は、コンポーネントの無名スロット (body) 内にレンダリングされます。
<my-article>
<span slot="title">タイトル欄のテキスト</span>
<span slot="meta">メタスロット用のテキスト</span>
<p>
スロット属性をつけていないテキスト 1。"body" div 内のデフォルト(名前のない)スロットに配置されます。
</p>
<p>
スロット属性をつけていないテキスト 2。"body" div 内のデフォルト(名前のない)スロットにも同時に配置されます。
</p>
</my-article>
結果
下記例では、各セクションに表示されるスロットのコンテンツが示されています。
無名スロットの代入
この例は、手動によるスロットの割り当てを示しています。
この手法では、それぞれの要素を HTMLSlotElement.assign() を使用して、具体的なスロットに手動で割り当てる必要があります。
デフォルトの割り当ては行われないため、割り当てられていないスロットは空になります。
HTML
まず、ブラウザーが slotAssignment: "manual" を対応していない場合に、JavaScript で表示させる非表示の対応に関する警告があります。
<p id="support-warning" hidden>
⛔ このブラウザーは手動によるスロットの割り当てに対応していません(名前付き割り当てが使用されています)。
</p>
次に、タイトル、メタデータ、本体コンテンツの子要素を持つ独自の要素 <my-article> を定義します。
それぞれの子要素は id によって識別されます。名前付きスロットの割り当てとは異なり、slot 属性は必要ありません。
<my-article>
<span id="text_title">タイトル欄のテキスト</span>
<span id="text_meta">メタスロット用のテキスト</span>
<p id="text_body_1">body スロットのテキスト 1</p>
<p id="text_body_2">body スロットのテキスト 1</p>
</my-article>
JavaScript
このカスタム要素は、slotAssignment: "manual" を指定してシャドウルートを設定します。
シャドウ DOM には、id で識別される名前のないスロットが含まれています。
assignSlots() メソッドは、軽量 DOM 要素をスロットに手動で代入します。
1 つのスロットに複数のノードを代入することができる点に注意してください。指定された順序がレンダリング順序を決定します。
class MyArticle extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open", slotAssignment: "manual" });
}
connectedCallback() {
this.render();
this.assignSlots();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.header {
background-color: plum;
}
.meta {
background-color: green;
}
.body {
background-color: lightblue;
}
</style>
<h2 class="header">
<slot id="titleSlot"></slot>
</h2>
<div class="meta">
<slot id="metaSlot"></slot>
</div>
<div class="body">
<slot id="bodySlot"></slot>
</div>
`;
}
assignSlots() {
// 1. スロットをターゲットにする
const titleSlot = this.shadowRoot.querySelector("#titleSlot");
const metaSlot = this.shadowRoot.querySelector("#metaSlot");
const bodySlot = this.shadowRoot.querySelector("#bodySlot");
// 2. 軽量 DOM 要素をターゲットにする
const titleText = this.querySelector("#text_title");
const metaText = this.querySelector("#text_meta");
const body1Text = this.querySelector("#text_body_1");
const body2Text = this.querySelector("#text_body_2");
// 3. 手動で割り当てる
titleSlot.assign(titleText);
metaSlot.assign(metaText);
bodySlot.assign(body2Text, body1Text);
}
}
customElements.define("my-article", MyArticle);
このコードは、ShadowRoot.slotAssignment プロパティが定義されているかどうかを検査し、定義されていない場合は警告を表示させます。
const isSlotAssignmentSupported = Object.hasOwn(
ShadowRoot.prototype,
"slotAssignment",
);
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isSlotAssignmentSupported);
結果
下記の例では、各セクションにスロットのコンテンツが表示されているはずです。
メモ:
手動によるスロットの割り当てに対応していない場合、警告が表示され、ブラウザーは named 割り当てを使用します。
ただし、軽量 DOM 要素にはいずれも slot 属性を持っていないため、それらはすべて最初の無名スロット(タイトルスロット)に挿入されます。
仕様書
| 仕様書 |
|---|
| DOM> # dom-element-attachshadow> |
ブラウザーの互換性
関連情報
ShadowRoot.modeShadowRoot.delegatesFocusShadowRoot.slotAssignment<template>要素 のshadowrootmode属性による宣言的なシャドウルートの設定- Declarative shadow DOM - web.dev (2023)