カスタム要素のライフサイクル、シャドウDOMのカプセル化、そしてスロットによるコンテンツ投影を習得します。
このレッスンを Kodokon で開くカスタム要素はHTMLの語彙を拡張します。HTMLElementを継承したクラスは、その名前にハイフンが含まれてさえいれば(user-badgeは良いがuserbadgeは不可)、CustomElementRegistryに登録できます。微妙な点: customElements.defineが呼ばれる前にタグがドキュメントに現れた場合、それはまず未知の要素として作成され、その後その場でアップグレードされます。コンストラクターは、すでにツリーに存在するノードに対して後から呼び出されるのです。
class UserBadge extends HTMLElement {
static observedAttributes = ["name"];
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
render() {
this.textContent = this.getAttribute("name");
}
}
customElements.define("user-badge", UserBadge);ライフサイクルにはいくつかの罠があります。attributeChangedCallbackはobservedAttributesにリストされた属性に対してのみ発火し、アップグレード中には、すでに存在するすべての属性についてconnectedCallbackの前に呼び出されます。仕様はコンストラクターが属性や子要素を検査すること、またそれらを追加することを禁じています。コンストラクターが実行される瞬間(createElementやクローンを介して)、要素は完全に空である可能性があるからです。
<template id="card-tpl">
<style>
:host { display: block; padding: 1rem; }
::slotted(h2) { margin: 0; }
</style>
<slot name="title"></slot>
<slot></slot>
</template>attachShadowはカプセル化されたサブツリーを作成します。スタイルはそこから漏れ出さず、ページのセレクターはその中に届きません。境界を越えるのは継承されるCSSプロパティ(color、font)とカスタムプロパティだけです。<template>の内容は.contentを介してアクセスできるDocumentFragmentに存在します。ドキュメントにクローンするまで、そのスクリプトは実行されず、画像も読み込まれません。ライトDOMに置かれたノードは<slot>要素へと投影されます。それらは物理的にはライトDOMに留まり、slotchangeイベントが割り当ての変更を通知します。
class InfoCard extends HTMLElement {
constructor() {
super();
const tpl = document.getElementById("card-tpl");
this.attachShadow({ mode: "open" })
.appendChild(tpl.content.cloneNode(true));
}
}
customElements.define("info-card", InfoCard);2つの拡張機能に注目する価値があります。フォーム関連カスタム要素: static formAssociated = trueとattachInternals()により、あなたのコンポーネントはフォームに完全に参加します(送信される値、ネイティブのバリデーション、:invalid状態)。そして宣言的シャドウDOM: <template shadowrootmode=open>はJavaScriptなしでパース時にシャドウルートを取り付け、Web componentsをサーバーサイドレンダリングと互換にします。
attachShadow({ mode: 'closed' })の場合、外部からelement.shadowRootは何を返しますか?null: attachShadowが返した参照だけがシャドウルートへのアクセスを与える<slot>に投影されたノードは物理的にはどこに存在しますか?