From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../ja/web/web_components/html_imports/index.html | 46 +++ files/ja/web/web_components/index.html | 218 ++++++++++++++ .../web_components/status_in_firefox/index.html | 58 ++++ .../using_custom_elements/index.html | 305 +++++++++++++++++++ .../web/web_components/using_shadow_dom/index.html | 218 ++++++++++++++ .../using_templates_and_slots/index.html | 324 +++++++++++++++++++++ 6 files changed, 1169 insertions(+) create mode 100644 files/ja/web/web_components/html_imports/index.html create mode 100644 files/ja/web/web_components/index.html create mode 100644 files/ja/web/web_components/status_in_firefox/index.html create mode 100644 files/ja/web/web_components/using_custom_elements/index.html create mode 100644 files/ja/web/web_components/using_shadow_dom/index.html create mode 100644 files/ja/web/web_components/using_templates_and_slots/index.html (limited to 'files/ja/web/web_components') diff --git a/files/ja/web/web_components/html_imports/index.html b/files/ja/web/web_components/html_imports/index.html new file mode 100644 index 0000000000..41d80f7a87 --- /dev/null +++ b/files/ja/web/web_components/html_imports/index.html @@ -0,0 +1,46 @@ +--- +title: HTML インポート +slug: Web/Web_Components/HTML_Imports +translation_of: Web/Web_Components/HTML_Imports +--- +

{{DefaultAPISidebar("Web Components")}}

+ +
+

Google Chrome 73 で廃止
+ この機能は廃止されました。まだいくつかのブラウザーで動作するかもしれませんが、いつ削除されてもおかしくないので、使わないようにしましょう。

+
+ +
+

Firefox はこの形式の HTML インポート は提供していません。詳細は状況更新を参照してください。標準化への同意が集まるか、代替機構が発表されるまで、Google の webcomponents.js などのポリフィルを使用することができます。

+
+ +

HTML インポートWeb Components のパッケージング機構として使用されることを意図していますが、単独で使用することもできます。

+ +

以下のように、HTML 文書中で <link> タグを使用してインポートします。

+ +
<link rel="import" href="myfile.html">
+ +

リンク種別 import は新設です。

+ +

仕様

+ + + + + + + + + + + + + + +
仕様状態備考
{{SpecName("HTML Imports")}}{{Spec2("HTML Imports")}}初回定義。
+ +

ブラウザーの互換性

+ + + +

{{Compat("html.elements.link.rel.import")}}

diff --git a/files/ja/web/web_components/index.html b/files/ja/web/web_components/index.html new file mode 100644 index 0000000000..79f4bbbe10 --- /dev/null +++ b/files/ja/web/web_components/index.html @@ -0,0 +1,218 @@ +--- +title: Web Components +slug: Web/Web_Components +tags: + - Landing + - NeedsTranslation + - TopicStub + - Web Components +translation_of: Web/Web_Components +--- +

{{DefaultAPISidebar("Web Components")}}

+ +
+

Web Components は、再利用可能なカスタム要素を作成し、ウェブアプリの中で利用するための、一連のテクノロジーです。コードの他の部分から独立した、カプセル化された機能を使って実現します。 

+
+ +

概念と使用法

+ + + +

開発者ならご存知でしょうが、可能な限りコードを再利用することは良い考えです。しかしこれは、以前から、カスタムのマークアップ構造にとって、それほど簡単なことではありませんでした。複雑な HTML (と一連のスタイルやスクリプト) を考えてみて下さい。ときに、カスタム UI の制御をレンダリングするために、コードを書かなければなりません。それに、注意していないと、それらの制御をどう使い回すかで、ページが複雑なものになってしまいます。

+ +

Web Components は、上記の問題の解決を目指しています。 Web Components は、3 つの主要な技術からなり、それらを組み合わせて、多目的なカスタム要素を作成します。カプセル化された機能を使うことで、コードの重複を恐れることなく、どこでも再利用することができます。

+ + + +

Web Component を実装する基本的な流れは、以下に挙げてある通りです。

+ +
    +
  1. Web Component の機能を明示したクラスもしくは関数を作成します。クラスを使用するなら、ECMAScript 2015 のクラスの文法に従って下さい。 (詳細はクラスを参照して下さい。)
  2. +
  3. 新しく作成したカスタム要素を登録します。{{domxref("CustomElementRegistry.define()")}} メソッドに、要素の名前、機能が明示されているクラスもしくは関数、またオプションでどの要素を継承するかを渡して下さい。
  4. +
  5. 必要なら、{{domxref("Element.attachShadow()")}} メソッドを使って、Shadow DOM をカスタム要素に紐付けます。通常の DOM メソッドを使って、子要素やイベントリスナーなどをShadow DOM に追加して下さい。
  6. +
  7. 必要なら、{{htmlelement("template")}} と {{htmlelement("slot")}} を使って、HTML テンプレートを定義します。通常の DOM メソッドを再度使って、テンプレートをクローンし、Shadow DOM に紐付けてください。
  8. +
  9. ページ内のお好きな場所で、通常の HTML 要素のように、カスタム要素を使って下さい。
  10. +
+ +

チュートリアル

+ +
+
カスタム要素を使ってみよう
+
単純な Web Component を作成するために、カスタム要素の機能の使い方を紹介したガイドラインです。それ以外にも、ライフサイクルコールバックやその他の高度な機能の中を覗いていきます。
+
Shadow DOM を使ってみよう
+
Shadow DOM の基礎を眺めるガイドラインです。 Shadow DOM を要素にどう紐付けるか、Shadow DOM ツリーにどう追加するか、どうスタイルするかなどを紹介しています。
+
テンプレートとスロットを使ってみよう
+
{{htmlelement("template")}} と {{htmlelement("slot")}} 要素を使って、再利用可能な HTML 構造体の定義と使用方法を紹介したガイドラインです。 
+
+ +

リファレンス

+ +

カスタム要素

+ +
+
{{domxref("CustomElementRegistry")}}
+
カスタム要素に関わる機能が含まれています。中でも注目すべきは、 {{domxref("CustomElementRegistry.define()")}} メソッドで、新しいカスタム要素を登録するために用います。それにより、カスタム要素をドキュメント内で使用できるようになります。
+
{{domxref("Window.customElements")}}
+
CustomElementRegistry オブジェクトへの参照を返します。
+
Life cycle callbacks
+
カスタム要素のクラス定義の中で定義された特別なコールバック関数で、挙動に影響を与えます。 +
    +
  • connectedCallback: カスタム要素がドキュメントの DOM に初めて接続したときに呼び出されます。
  • +
  • disconnectedCallback: カスタム要素がドキュメントの DOM から切断されたときに呼び出されます。
  • +
  • adoptedCallback: カスタム要素が新しいドキュメントに移動したときに呼び出されます。
  • +
  • attributeChangedCallback: カスタム要素の属性のひとつが追加、削除、もしくは変更されたときに呼び出されます。
  • +
+
+
+
    +
+
+
+ +
+
カスタムビルトイン要素を作成するための拡張機能
+
+
    +
  • {{htmlattrxref("is")}} グローバル HTML 属性: 標準の HTML 要素が、カスタムビルトイン要素のように振る舞うべきかを指定できます。
  • +
  • {{domxref("Document.createElement()")}} メソッドの "is" オプション: カスタムビルトイン要素のように振る舞う標準の HTML 要素のインスタンスを作成できます。
  • +
+
+
CSS の擬似クラス
+
カスタム要素に関連する擬似クラス: +
    +
  • {{cssxref(":defined")}}: ビルトイン要素と CustomElementRegistry.define() で定義されるカスタム要素を含む、あらゆる定義済みの要素にマッチします。
  • +
  • {{cssxref(":host")}}: 使われている CSS を含む、Shadow DOM のシャドーホストを選択します。
  • +
  • {{cssxref(":host()")}}: 使われている CSS を含む、Shadow DOM のシャドーホストを選択します。 (Shadow DOM の内側からカスタム要素を選択することができます。) ただし、関数のパラメータとして渡されるセレクタがシャドーホストに一致している場合に限ります。
  • +
  • {{cssxref(":host-context()")}}: 使われている CSS を含む、Shadow DOM のシャドーホストを選択します。 (Shadow DOM の内側からカスタム要素を選択することができます。) ただし、関数のパラメータとして渡されるセレクタが DOM 階層内のシャドーホストの先祖要素に一致している場合に限ります。
  • +
+
+
+ +

Shadow DOM

+ +
+
{{domxref("ShadowRoot")}}
+
Represents the root node of a shadow DOM subtree.
+
{{domxref("DocumentOrShadowRoot")}}
+
A mixin defining features that are available across document and shadow roots.
+
{{domxref("Element")}} extensions
+
Extensions to the Element interface related to shadow DOM: +
    +
  • The {{domxref("Element.attachShadow()")}} method attaches a shadow DOM tree to the specified element.
  • +
  • The {{domxref("Element.shadowRoot")}} property returns the shadow root attached to the specified element, or null if there is no shadow root attached.
  • +
+
+
Relevant {{domxref("Node")}} additions
+
Additions to the Node interface relevant to shadow DOM: +
    +
  • The {{domxref("Node.getRootNode()")}} method returns the context object's root, which optionally includes the shadow root if it is available.
  • +
  • The {{domxref("Node.isConnected")}} property returns a boolean indicating whether or not the Node is connected (directly or indirectly) to the context object, e.g. the {{domxref("Document")}} object in the case of the normal DOM, or the {{domxref("ShadowRoot")}} in the case of a shadow DOM.
  • +
+
+
{{domxref("Event")}} extensions
+
Extensions to the Event interface related to shadow DOM: +
    +
  • {{domxref("Event.composed")}}: Returns a {{jsxref("Boolean")}} which indicates whether the event will propagate across the shadow DOM boundary into the standard DOM (true), or not  (false).
  • +
  • {{domxref("Event.composedPath")}}: Returns the event’s path (objects on which listeners will be invoked). This does not include nodes in shadow trees if the shadow root was created with {{domxref("ShadowRoot.mode")}} closed.
  • +
+
+
+ +

HTML templates

+ +
+
{{htmlelement("template")}}
+
Contains an HTML fragment that is not rendered when a containing document is initially loaded, but can be displayed at runtime using JavaScript, mainly used as the basis of custom element structures. The associated DOM interface is {{domxref("HTMLTemplateElement")}}.
+
{{htmlelement("slot")}}
+
A placeholder inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together. The associated DOM interface is {{domxref("HTMLSlotElement")}}.
+
The slot global HTML attribute
+
Assigns a slot in a shadow DOM shadow tree to an element.
+
{{domxref("Slotable")}}
+
A mixin implemented by both {{domxref("Element")}} and {{domxref("Text")}} nodes, defining features that allow them to become the contents of an {{htmlelement("slot")}} element. The mixin defines one attribute, {{domxref("Slotable.assignedSlot")}}, which returns a reference to the slot the node is inserted in.
+
+ +
+
{{domxref("Element")}} extensions
+
Extensions to the Element interface related to slots: +
    +
  • {{domxref("Element.slot")}}: Returns the name of the shadow DOM slot attached to the element.
  • +
+
+
CSS pseudo-elements
+
Pseudo-elements relating specifically to slots: +
    +
  • {{cssxref("::slotted")}}: Matches any content that is inserted into a slot.
  • +
+
+
The {{event("slotchange")}} event
+
Fired on an {{domxref("HTMLSlotElement")}} instance ({{htmlelement("slot")}} element) when the node(s) contained in that slot change.
+
+ +

+ +

web-components-examples の GitHub レポジトリに、いくつかの例を用意してあります。時間とともに、より多くの例が追加されることでしょう。

+ +

仕様

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
仕様ステータスコメント
{{SpecName("HTML WHATWG","scripting.html#the-template-element","<template> element")}}{{Spec2('HTML WHATWG')}}{{HTMLElement("template")}} の定義です。
{{SpecName("HTML WHATWG","custom-elements.html#custom-elements","custom elements")}}{{Spec2('HTML WHATWG')}}HTML Custom Elements の定義です。
{{SpecName("DOM WHATWG","#shadow-trees","shadow trees")}}{{Spec2('DOM WHATWG')}}Shadow DOM の定義です。
{{SpecName("HTML Imports", "", "")}}{{Spec2("HTML Imports")}}HTML Imports の最初の定義です。
{{SpecName("Shadow DOM", "", "")}}{{Spec2("Shadow DOM")}}Shadow DOM の最初の定義です。
+ +

ブラウザのサポート

+ + + +

一般に、

+ + + +

特定の機能のブラウザでの実装状況は、上記のページを調べるようにして下さい。

+ + + +

参考

+ + diff --git a/files/ja/web/web_components/status_in_firefox/index.html b/files/ja/web/web_components/status_in_firefox/index.html new file mode 100644 index 0000000000..0fa1b2248b --- /dev/null +++ b/files/ja/web/web_components/status_in_firefox/index.html @@ -0,0 +1,58 @@ +--- +title: Firefox での Web Components のサポート状況 +slug: Web/Web_Components/Status_in_Firefox +tags: + - API + - Experimental + - Firefox + - Guide + - Web Components + - status +translation_of: Web/Web_Components/Status_in_Firefox +--- +

{{DefaultAPISidebar("Web Components")}}{{SeeCompatTable}}

+ +

Web Components は、とても新しい技術で、ブラウザ実装者や Web 開発者が実際に利用した経験を集めて仕様を考案しています。実装状況は変化しやすく、次々と進化していきます。この記事は、Firefox や Firefox OS で使用されている Gecko での実装状況の一覧を示します。

+ +
+
+

ネイティブサポート

+ +

Firefox と Firefox OS では、以下の機能が実装されており、デフォルトで有効です:

+ +
    +
  • {{HTMLElement("template")}}
  • +
+ +

今後実装予定の機能

+ +
    +
  • 新しい同意に基づいた Shadow DOM の実装は、2016 年 Q1 にリリース予定です。AnneWilson のブログ投稿に詳細が記述されています。しかし、まだ仕様について 多くの議論や課題 があり、すべてのブラウザへの実装は将来となるでしょう。
  • +
  • Custom elements は、最初からやり直しで、ECMAScript 6 の文法を使用してリビルドする計画 (つまり、より少ないプロトタイプを基に作成) です。Apple の Ryosuke Niwa が、実装をいくつか具体化しています。 +
      +
    • 古い文法は、しばらくの間、新しい文法と共に Chrome で動作するでしょう (例えば、{{domxref("Element.attachShadow()")}} に対して {{domxref("Element.createShadowRoot()")}})、しかし、Firefox ではネイティブでは動作しないでしょう。
    • +
    +
  • +
  • これらの問題について、2016 年 1 月の会議 でベンダが議論するでしょう。
  • +
+ +

放棄された機能

+ +

これらの機能は、実装の検討がされており、実験的に実装されていたものもあります。今後は改良もされず、削除されるでしょう。

+ +
    +
  • HTML imports の使用は、ES6 モジュールで開発者が何ができるかを確認することは、待って欲しいです (まだ実装されていません。{{bug(568953)}} をご覧ください)。Firefox から削除される予定の未完了の import の実装がありました。
  • +
+ +

Firefox でポリフィルを使用する

+ +

Firefox でポリフィルを使用する際に考慮すべき注意事項があります:

+ +
    +
  • about:config の {{pref("dom.webcomponents.enabled")}} 設定を true に変更して Firefox で Web Components を有効にすると、完全でないネイティブ実装が動き始め、ポリフィルが混乱する可能性があります。
  • +
  • webcomponents.js ポリフィルを使用した Shadow DOM のポリフィルは、スタイルをカプセル化できません。そのため、スタイルは bleed through でしょう。ポリフィルを使用して構築されたサイトは、ネイティブの Shadow DOM を サポートした 環境と異なる見た目になることに注意してください。
  • +
  • Shadow DOM のポリフィルは、機能にフックするために DOM 要素のプロトタイプをリライトするため、とても動作が遅いです (ポリフィルというよりポリリプレイスです!)。
  • +
  • Shadow DOM を使用する必要がない場合、webcomponents.js ポリフィルの webcomponents-lite.js バージョンを使用することをお勧めします。このバージョンは、Shadow DOM を使用しないポリフィルです。
  • +
+
+
diff --git a/files/ja/web/web_components/using_custom_elements/index.html b/files/ja/web/web_components/using_custom_elements/index.html new file mode 100644 index 0000000000..c19f2f73c9 --- /dev/null +++ b/files/ja/web/web_components/using_custom_elements/index.html @@ -0,0 +1,305 @@ +--- +title: Using custom elements +slug: Web/Web_Components/Using_custom_elements +tags: + - Classes + - Guide + - HTML + - Web Components + - autonomous + - custom elements + - customized +translation_of: Web/Web_Components/Using_custom_elements +--- +
{{DefaultAPISidebar("Web Components")}}
+ +

Webコンポーネント標準の重要な特徴の一つはカスタム要素を作れることです。それはページの機能を提供する長くネストした要素のバッチではなく、HTMLページ上で機能をカプセル化します。
+ この記事はカスタム要素APIの使い方を紹介します。

+ +
+

: カスタム要素をデフォルトでサポートするのは Firefox, Chrome, と Edge (76)です。Opera と Safari は今のところ、自律カスタム要素のみサポートしています。

+
+ +

High-level view

+ +

Webドキュメント上でカスタム要素をコントロールするのは {{domxref("CustomElementRegistry")}} オブジェクトです。 — このオブジェクトはページへのカスタム要素を登録したり、どんなカスタム要素が登録されているのかを返すなどの操作を行えます。

+ +

ページにカスタム要素を登録するには,  {{domxref("CustomElementRegistry.define()")}} メソッドを使います。次の引数を取ることができます:

+ + + +

例えば、次の様に word-count 要素 を定義できます:

+ +
customElements.define('word-count', WordCount, { extends: 'p' });
+ +

word-count要素は WordCountクラスのオブジェクトで {{htmlelement("p")}}要素を拡張します。

+ +

カスタム要素のクラスオブジェクトは ES 2015 のクラスシンタックスで実装します。例えば、WordCount 次の様になります:

+ +
class WordCount extends HTMLParagraphElement {
+  constructor() {
+    // Always call super first in constructor
+    super();
+
+    // Element functionality written in here
+
+    ...
+  }
+}
+ +

これはごく簡単な例ですが、ここでできることはもっとあります。クラスの中でライフサイクルコールバックを定義することができ、要素のライフサイクルの特定のポイントで実行されます。例えば、connectedCallback はドキュメント接続要素にカスタム要素が追加されるたびに実行されます。一方 attributeChangedCallback はカスタム要素に属性が追加、削除、変更される時に実行されます。

+ +

{{anch("Using the lifecycle callbacks")}} でこれらについてもっと学ぶことができます。.

+ +

カスタム要素には2つのタイプがあります:

+ + + +

簡単な例と実践

+ +

ここで、どのようにカスタム要素をを作るのかを詳細に説明するために簡単な例を見てみましょう。

+ +

自律カスタム要素

+ +

自律カスタム要素の例を見てみましょう — <popup-info-box>実例参照). これは画像とテキストを受け取り、ページにアイコンを埋め込みます。アイコンにフォーカスすると、ポップアップする情報ボックスにテキストを表示してコンテキスト内の情報を更に提供します。

+ +

最初に{{domxref("HTMLElement")}}を継承して PopUpInfo,というクラスを定義します。 自律カスタム要素はほぼいつも HTMLElementを継承します。

+ +
class PopUpInfo extends HTMLElement {
+  constructor() {
+    // Always call super first in constructor
+    super();
+
+    // write element functionality in here
+
+    ...
+  }
+}
+ +

前述のコードスニペットはクラスの constructor() の定義を含んでおり、常に super() を最初に呼び出します。これにより正しいプロタイプチェーンが確立されます。

+ +

コンストラクタ内で、インスタンス化された時に要素が持っているすべての機能を定義します。この例ではカスタム要素にshadowルートをアタッチしています。DOM操作を行い、要素内部の shadow DOM構造を作ります。—DOM構造はshadowルートにアタッチされます— そして最後にスタイルを適用するためにCSSをshadowルートにアタッチします。

+ +
// Create a shadow root
+var shadow = this.attachShadow({mode: 'open'});
+
+// Create spans
+var wrapper = document.createElement('span');
+wrapper.setAttribute('class','wrapper');
+var icon = document.createElement('span');
+icon.setAttribute('class','icon');
+icon.setAttribute('tabindex', 0);
+var info = document.createElement('span');
+info.setAttribute('class','info');
+
+// Take attribute content and put it inside the info span
+var text = this.getAttribute('text');
+info.textContent = text;
+
+// Insert icon
+var imgUrl;
+if(this.hasAttribute('img')) {
+  imgUrl = this.getAttribute('img');
+} else {
+  imgUrl = 'img/default.png';
+}
+var img = document.createElement('img');
+img.src = imgUrl;
+icon.appendChild(img);
+
+// Create some CSS to apply to the shadow dom
+var style = document.createElement('style');
+
+style.textContent = '.wrapper {' +
+// CSS truncated for brevity
+
+// attach the created elements to the shadow dom
+
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);
+ +

最後に、カスタム要素を CustomElementRegistry に登録します。前述の define() を使用して、パラメーターで要素名とその機能を定義するクラス名を指定します:

+ +
customElements.define('popup-info', PopUpInfo);
+ +

これによってページで使えるようになりました。HTML中で下記のように使用することができます。

+ +
<popup-info img="img/alt.png" 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>
+ +
+

: こちらで 完全なソースコード を見ることができます。

+
+ +
+

: カスタム要素が機能するためには、DOMの解析を終えた後にカスタム要素を登録するスクリプトが読み込まれる必要があることに注意してください。これは <script> 要素を <body> 要素内の最下部に配置する、または <script> 要素に defer 属性を加えることで解決します。

+
+ +

内部スタイル 対 外部スタイル

+ +

上記の例では {{htmlelement("style")}} 要素を用いてShadow DOMにスタイルを適用しました。しかし、{{htmlelement("link")}} 要素から外部のスタイルシートを参照することも可能です。

+ +

例えば、popup-info-box-external-stylesheet のコードを少し見てみましょう(ソースコード)。

+ +
// Apply external styles to the shadow dom
+const linkElem = document.createElement('link');
+linkElem.setAttribute('rel', 'stylesheet');
+linkElem.setAttribute('href', 'style.css');
+
+// Attach the created element to the shadow dom
+shadow.appendChild(linkElem);
+
+ +

この手法が特に大規模なスタイルシートで推奨されます。これによって、綺麗で、より共有しやすい効率の良いコードになります。

+ +

カスタマイズされたビルトイン要素

+ +

ここで、もう1つのビルトイン要素の例を見てみましょう — expanding-list (デモはこちら) 。 これにより番号なしリストが展開・縮小するメニューになります。
+
+ まず始めに、これまでと同様の規則でクラス要素を定義します。

+ +
class ExpandingList extends HTMLUListElement {
+  constructor() {
+    // Always call super first in constructor
+    super();
+
+    // write element functionality in here
+
+    ...
+  }
+}
+ +

ここでは要素の詳細な機能については説明しませんが、ソースコードからどのように動作するのかチェックすることができます。これまでと唯一違う点は {{domxref("HTMLElement")}} ではなく、 {{domxref("HTMLUListElement")}} インターフェースを拡張していることです。そのため、独立した要素ではなく、 {{htmlelement("ul")}} 要素の特徴を備えた上に、私たちが定義した機能を持っています。これこそが自律カスタム要素ではなくカスタマイズされたビルトイン要素である理由です。
+
+ 次に、以前と同様に define() を用いて要素を登録するのですが、今回はこのカスタム要素がどの要素から継承したのかという情報をオプションとして渡しています。

+ +
customElements.define('expanding-list', ExpandingList, { extends: "ul" });
+ +

Webドキュメント内でビルトイン要素を使用する場合とはやや異なります。

+ +
<ul is="expanding-list">
+
+  ...
+
+</ul>
+ +

通常のように <ul> を使用していますが、カスタム要素の名前が is 属性で指定されています。

+ +
+

: もう一度述べますが、こちらで 完全なソースコード を見ることができます。

+
+ +

ライフサイクルコールバックの使用

+ +

カスタム要素のクラス定義内にいくつかの異なるコールバックを定義できます。これらのコールバックは、要素のライフサイクルのさまざまな時点で起動します。

+ + + +

使用中のこれらの例を見てみましょう。以下のコードは、ライフサイクルコールバックの例から取ったものです(ライブ実行を参照)。これは、ページ上に固定サイズの色付きの正方形を生成する単純な例です。カスタム要素は次のようになります。

+ +
<custom-square l="100" c="red"></custom-square>
+ +

クラスコンストラクターは非常に単純です。ここでは、要素にシャドウDOMをアタッチし、空の{{htmlelement("div")}} および{{htmlelement("style")}} 要素をシャドウルートにアタッチします:

+ +
var shadow = this.attachShadow({mode: 'open'});
+
+var div = document.createElement('div');
+var style = document.createElement('style');
+shadow.appendChild(style);
+shadow.appendChild(div);
+ +

この例の主要な機能はupdateStyle()です。これは要素を取得し、シャドウルートを取得し、その<style>要素を見つけて、{{cssxref("width")}}, {{cssxref("height")}}, および{{cssxref("background-color")}} をスタイルに追加します。

+ +
function updateStyle(elem) {
+  const shadow = elem.shadowRoot;
+  shadow.querySelector('style').textContent = `
+    div {
+      width: ${elem.getAttribute('l')}px;
+      height: ${elem.getAttribute('l')}px;
+      background-color: ${elem.getAttribute('c')};
+    }
+  `;
+}
+ +

実際の更新はすべて、メソッドとしてクラス定義内に配置されるライフサイクルコールバックによって処理されます。 connectedCallback()は、要素がDOMに追加されるたびに実行されます。ここでは、updateStyle()関数を実行して、正方形がその属性で定義されたスタイルになっていることを確認します。

+ +
connectedCallback() {
+  console.log('Custom square element added to page.');
+  updateStyle(this);
+}
+ +

disconnectCallback()およびadoptedCallback() コールバックは、要素がDOMから削除されるか、別のページに移動されたときに通知する単純なメッセージをコンソールに記録します。

+ +
disconnectedCallback() {
+  console.log('Custom square element removed from page.');
+}
+
+adoptedCallback() {
+  console.log('Custom square element moved to new page.');
+}
+ +

attributeChangedCallback()コールバックは、要素の属性の1つが何らかの方法で変更されるたびに実行されます。そのプロパティからわかるように、属性、属性の名前、および古い属性値と新しい属性値を個別に操作することができます。ただし、この場合は、updateStyle()関数を再度実行して、新しい値に従って正方形のスタイルが更新されるようにします。

+ +
attributeChangedCallback(name, oldValue, newValue) {
+  console.log('Custom square element attributes changed.');
+  updateStyle(this);
+}
+ +

属性が変更されたときに起動するattributeChangedCallback()コールバックを取得するには、属性を監視する必要があることに注意してください。これは、カスタム要素クラス内でstatic get observedAttributes()メソッドを指定することによって行われます-これは、監視したい属性の名前を含む配列を返します:

+ +
static get observedAttributes() { return ['c', 'l']; }
+ +

この例では、これはコンストラクターの最上部に配置されています。

+ +
+

: ここで完全なJavaScriptソースを検索してください。

+
+ +

ポリフィル 対 クラス

+ +

カスタム要素のポリフィルは HTMLElement などのネイティブのコンストラクタに対してパッチを当てることで、単にネイティブのコンストラクタが作成したものとは異なるインスタンスを返すことがあります。
+
+ もし constructor や強制的に super を呼び出す必要があるなら、任意の引数を渡して super を呼び出した結果を返すことを忘れないでください。

+ +
class CustomElement extends HTMLElement {
+  constructor(...args) {
+    const self = super(...args);
+    // self functionality written in here
+    // self.addEventListener(...)
+    // return the right context
+    return self;
+  }
+}
+ +

もしコンストラクタ内で何も処理が必要ないならば、単に省略することでネイティブの挙動を維持できます。

+ +
 constructor(...args) { return super(...args); }
+
+ +

トランスパイラ 対 クラス

+ +

レガシーなブラウザをターゲットとしたBabel 6またはTypeScriptでは、ES2015のクラス構文は期待通りにトランスパイルされない可能性があることに注意してください。Babel 7もしくはBabel 6の babel-plugin-transform-builtin-classesを使用して、レガシーなブラウザではなくくTypeScriptでES2015をターゲットとすることができます。

+ +

ライブラリ

+ +

カスタム要素を作る際に抽象度を高めることを目的とした、Web Componentsで実装されたライブラリがあります。その内のいくつかを挙げます。snuggsi ツX-TagSlim.jsLitElementSmartStencil

diff --git a/files/ja/web/web_components/using_shadow_dom/index.html b/files/ja/web/web_components/using_shadow_dom/index.html new file mode 100644 index 0000000000..3a990d1953 --- /dev/null +++ b/files/ja/web/web_components/using_shadow_dom/index.html @@ -0,0 +1,218 @@ +--- +title: shadow DOM の使い方 +slug: Web/Web_Components/Using_shadow_DOM +translation_of: Web/Web_Components/Using_shadow_DOM +--- +
 {{DefaultAPISidebar("Web Components")}}
+ +

Web コンポーネントにおいてカプセル化 (構造やスタイル、挙動を隠し、同じページの他のコードと分離すること) は重要です。これにより他の場所でのクラッシュを防ぎ、またコードが綺麗になります。Shadow DOM API はこの隠され分離された DOM を付加するための方法を提供しています。この記事では Shadow DOM を使う基本を記述しています。

+ +
+

Note: Shadow DOM は Firefox (version 63以降)、Chrome、Opera、そして Safari でサポートされています。 Chromiumベースの新しいEdge(75以降)もサポートしています。旧Edgeはサポートしていません。

+
+ +

High-level view

+ +

この記事では DOM (Document Object Model) —ドキュメントにある要素やテキストを表現するノードによって構成された木構造 — に親しんでいる前提で説明します。例として以下の HTML フラグメントを考えます。 

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Simple DOM example</title>
+  </head>
+  <body>
+      <section>
+        <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
+        <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
+      </section>
+  </body>
+</html>
+ +

このフラグメントによって以下のような DOM 構造が構成されます。

+ +

+ +

Shadow DOM により、通常の DOM ツリーの要素の下に DOM ツリーを追加し隠すことができます。shadow DOM ツリーは shadow root を根とし、その下には普通の DOM ツリーと同様に任意の要素を追加できます。

+ +

+ +

以下に shadow DOM における用語を定義します。

+ + + +

shadow DOM 内のノードは、子ノードを追加したり属性付けをしたり、個々のノードのスタイルを element.style.foo と定めたり shadow DOM ツリー全体のスタイルを {{htmlelement("style")}} 要素で定めたりなど、通常の DOM のノードと同様に制御できます。ただし、shadow DOM の内部コードによって外部を制御することは出来ません。 

+ +

shadow DOM は全く新しいものではなく、例えばブラウザにおいて要素の内部構造をカプセル化するために長年使用されていました。 {{htmlelement("video")}} 要素の例を考えます。DOM で見えるものは <video> 要素のみですが、その shadow DOM の内部ではたくさんのボタンや他の制御コードが含まれています。shadow DOM スペックができたことにより、この機能を実際に操作しカスタム要素で shadow DOM を作ることができるようになりました。 

+ +

基本的な使い方

+ +

shadow root は {{domxref("Element.attachShadow()")}} メソッドを利用して任意の要素に追加することができます。このメソッドではパラメータとして mode オプションを open または closed の値で取ります。 

+ +
let shadow = elementRef.attachShadow({mode: 'open'});
+let shadow = elementRef.attachShadow({mode: 'closed'});
+ +

open の場合は shadow DOM の内部にメインページに書かれた JavaScript からアクセスできます。以下のように {{domxref("Element.shadowRoot")}} プロパティを利用してアクセスできます。

+ +
let myShadowDom = myCustomElem.shadowRoot;
+ +

shadow root を mode: closed で追加した場合、外部から shadow DOM にアクセス出来ず、myCustomElem.shadowRoot は null を返します。<video> などの shadow DOM を含む既成の要素は closed になっています。

+ +
+

Note: このブログ記事を見るに、実はclosed shadow DOMを回避するのはそんなに難しいことではなく、また、これを完全に隠すことはその価値の割には面倒です。

+
+ +

shadow DOM をコンストラクタの一部としてカスタム要素に追加することを考えます。

+ +
let shadow = this.attachShadow({mode: 'open'});
+ +

shadow DOM は、通常の DOM の操作に使われる DOM API で操作することができます。

+ +
var para = document.createElement('p');
+shadow.appendChild(para);
+// etc.
+ +

実用例

+ +

カスタム要素内のシンプルなshadow DOM を見てみましょう — <popup-info-box> (実行例)。このタグはイメージアイコンとテキストを取り、アイコンをページに埋め込みます。アイコンがフォーカスされるとポップアップが表示され、さらなる情報を提供します。
+ まずは HTMLElement を拡張して PopUpInfo というクラスを定義します。

+ +
class PopUpInfo extends HTMLElement {
+  constructor() {
+    // Always call super first in constructor
+    super();
+
+    // write element functionality in here
+
+    ...
+  }
+}
+ +

クラス定義の際、インスタンスが初期化された時に用意されるあらゆる関数を定義したコンストラクタを定義します。

+ +

shadow root の作成

+ +

最初に shadow root をカスタム要素に追加します。

+ +
// Create a shadow root
+var shadow = this.attachShadow({mode: 'open'});
+ +

shadow DOM 構造の作成

+ +

次に shadow DOM 構造を作ります。

+ +
// Create spans
+var wrapper = document.createElement('span');
+wrapper.setAttribute('class','wrapper');
+var icon = document.createElement('span');
+icon.setAttribute('class','icon');
+icon.setAttribute('tabindex', 0);
+var info = document.createElement('span');
+info.setAttribute('class','info');
+
+// Take attribute content and put it inside the info span
+var text = this.getAttribute('text');
+info.textContent = text;
+
+// Insert icon
+var imgUrl;
+if(this.hasAttribute('img')) {
+  imgUrl = this.getAttribute('img');
+} else {
+  imgUrl = 'img/default.png';
+}
+var img = document.createElement('img');
+img.src = imgUrl;
+icon.appendChild(img);
+
+ +

shadow DOM のスタイル

+ +

そのあと、 {{htmlelement("style")}} 要素を作り CSS でスタイルを付けます。

+ +
// Create some CSS to apply to the shadow dom
+var 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;
+}`;
+
+
+ +

shadow DOM を shadow root に追加

+ +

最後に作成した全ての要素を shadow root に追加します。

+ +
// attach the created elements to the shadow dom
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);
+ +

カスタム要素の使用

+ +

クラスを定義すると、定義したようにカスタム要素を使用することができます。(Using custom elements)

+ +
// Define the new element
+customElements.define('popup-info', PopUpInfo);
+ +
<popup-info img="img/alt.png" 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.">
+ +
+

内部スタイル vs 外部スタイル

+ +

上述の例では{{htmlelement("style")}}要素を用いてShadow DOMにスタイルを適用しましたが、代わりに{{htmlelement("link")}}要素で外部のスタイルシートを参照することでも完全に実現できます。

+ +

例として、popup-info-box-external-stylesheet の例を見てください。(ソースコード)

+ +
// Apply external styles to the shadow dom
+const linkElem = document.createElement('link');
+linkElem.setAttribute('rel', 'stylesheet');
+linkElem.setAttribute('href', 'style.css');
+
+// Attach the created element to the shadow dom
+shadow.appendChild(linkElem);
+ +

 {{htmlelement("link")}}要素がshadow rootの描画をブロックしないため、スタイルシートがロードされている間flash of unstyled content(FOUC)が起こりうることに注意してください。

+ +

多くのモダンブラウザは、共通ノードからコピーされた、または同一のテキストを持つ {{htmlelement("style")}}タグの最適化を実装して、単一のバッキングスタイルシートを共有できるようにしています。この最適化により、外部スタイルと内部スタイルのパフォーマンスは同様になります。

+ +

参考

+ + +
diff --git a/files/ja/web/web_components/using_templates_and_slots/index.html b/files/ja/web/web_components/using_templates_and_slots/index.html new file mode 100644 index 0000000000..71f1ac7ca7 --- /dev/null +++ b/files/ja/web/web_components/using_templates_and_slots/index.html @@ -0,0 +1,324 @@ +--- +title: template と slot の使い方 +slug: Web/Web_Components/Using_templates_and_slots +translation_of: Web/Web_Components/Using_templates_and_slots +--- +
{{DefaultAPISidebar("Web Components")}}
+ +

この記事では shadow DOM で使われる {{htmlelement("template")}} と {{htmlelement("slot")}} 要素の使い方を説明します。

+ +

テンプレートの真実

+ +

あるWebページ上で同じ構造を繰り返し使用する必要がある場合、同じ実装を繰り返し書くよりも、テンプレートのようなものを作って利用する方が合理的でしょう。これは以前から可能でしたが、{{htmlelement("template")}} 要素より簡単に使えるようになりました。 この要素と中身は DOM 上ではレンダリングされませんが、JavaScript から参照することができます。

+ +

以下の簡単なサンプルを見てみましょう。

+ +
<template id="my-paragraph">
+  <p>My paragraph</p>
+</template>
+ +

テンプレートの内部はページには表示されません。以下のコードのようにJavaScript を用いて参照を取り DOM に追加するとページ上に表示できます。

+ +
let template = document.getElementById('my-paragraph');
+let templateContent = template.content;
+document.body.appendChild(templateContent);
+ +

つまらない例ですがすでに有用性は見えてきたでしょう。

+ +

Web Componentsを利用したテンプレートの使用

+ +

テンプレートはそれ自身でも有用ですが web コンポーネントと共に使用することでより上手く使えます。テンプレートを shadow DOM として活用する web コンポーネントを <my-paragraph> と名付け定義しましょう。

+ +
customElements.define('my-paragraph',
+  class extends HTMLElement {
+    constructor() {
+      super();
+      let template = document.getElementById('my-paragraph');
+      let templateContent = template.content;
+
+      const shadowRoot = this.attachShadow({mode: 'open'})
+        .appendChild(templateContent.cloneNode(true));
+  }
+})
+ +

ここで、テンプレートの内容を使用するために {{domxref("Node.cloneNode()")}} を使用してクローンしたものを shadow root に追加していることに注意してください。

+ +

テンプレートの内容を shadow DOM に追加しているので、テンプレートの内部に {{htmlelement("style")}} 要素を用意しスタイルを含むことができます。このスタイルはカスタム要素の内部でカプセル化されます。これは通常の DOM に追加するだけでは正しく動きません。

+ +

したがって、例えば

+ +
<template id="my-paragraph">
+  <style>
+    p {
+      color: white;
+      background-color: #666;
+      padding: 5px;
+    }
+  </style>
+  <p>My paragraph</p>
+</template>
+ +

こうすれば HTML ドキュメントに以下を追加することで使用できます。

+ +
<my-paragraph></my-paragraph>
+
+ +
+

注意: テンプレートはブラウザでよくサポートされています。Shadow DOM APIはデフォルトのFirefox (バージョン63以降) 、Chrome、OperaそしてSafariでサポートされています。Edgeでも現在開発が行われています。

+
+ +

スロットによる柔軟性の強化

+ +

ここまでのサンプルでは高々1種類のテキストを表示できるのみで、普通の paragraph よりも使えません。{{htmlelement("slot")}} 要素を用いることで、各要素のインスタンスに異なるテキストを表示させることができます。{{htmlelement("slot")}}  は {{htmlelement("template")}} よりサポートが限られており、Chrome 53以降、Opera 40以降、Safari 10以降、Firefox 59以降で実装されていますが、Edge ではまだサポートされていません。slot はその name 属性で区別されており、template の中で任意のマークアップで slot の内容のデフォルト値を埋めることができます。

+ +

上記の例に slot を追加することを考えます。パラグラフの要素を以下のように書くことができます。

+ +
<p><slot name="my-text">デフォルトテキスト</slot></p>
+ +

slot の内容が定義されていない場合や、ブラウザが slot をサポートしていな場合は <my-paragraph> は fallback コンテンツを保持し、このサンプルの場合では "デフォルトテキスト" を表示させることになります。

+ +

内容を定義したい slot の名前を {{htmlattrxref("slot")}} 属性に設定した要素を <my-paragraph> の中に用意すると、その中身が slot の内容になります。中身は HTML 構造を持つ任意のもので埋めることができます。

+ +
<my-paragraph>
+  <span slot="my-text">新しいテキストを代入します</span>
+</my-paragraph>
+ +

以下のようにも設定できます。

+ +
<my-paragraph>
+  <ul slot="my-text">
+    <li>新しいテキストを代入します</li>
+    <li>リストも代入できます</li>
+  </ul>
+</my-paragraph>
+
+ +
+

注意: スロットに挿入できるのは {{domxref("Slotable")}} な要素に限られます; 要素がスロットに挿入されたとき、slotted と呼ばれます。

+
+ +

簡単なサンプルでの説明は以上です。他にも実装してみたい場合は、GitHub上のサンプルコードをご利用ください(実行例)。

+ +

より踏み込んだ例

+ +

他の例もみてみましょう。

+ +

これからのコードは {{htmlelement("slot")}} を {{htmlelement("template")}} と共に使用する方法の例です。以下の2点を目指す JavaScript です。

+ + + +

{{htmlelement("slot")}} 要素は {{htmlelement("template")}} 要素なしで使用することが可能です。例えば、 {{HTMLElement("div")}} 要素の中で宣言しても Shadow DOM で使用した場合と同様にプレースホルダーとしての役割は果たします。しかし、{{HTMLElement("template")}} 要素の中で使用する方がより一般的で実用的です。

+ +

テンプレートを利用したコンテナの目的は {{HTMLElement("template")}} を使用することで意味的にわかりやすくすることです。さらに、{{HTMLElement("template")}} の中には {{HTMLElement("td")}} など直接追加して良い要素があり、これらは {{HTMLElement("div")}} 要素の中に追加された場合は消えます。

+ +
+

注意: element-detailsの完全なコードはここから見ることができます (実行例)。

+
+ +

template をスロットと共に作る

+ +

まず最初に{{htmlelement("template")}} 要素の中に {{htmlelement("slot")}} 要素を作成し、新しい "element-details-template" と名付けたフラグメントを作ります。

+ +
<template id="element-details-template">
+  <style>
+  details {font-family: "Open Sans Light",Helvetica,Arial}
+  .name {font-weight: bold; color: #217ac0; font-size: 120%}
+  h4 { margin: 10px 0 -8px 0; }
+  h4 span { background: #217ac0; padding: 2px 6px 2px 6px }
+  h4 span { border: 1px solid #cee9f9; border-radius: 4px }
+  h4 span { color: white }
+  .attributes { margin-left: 22px; font-size: 90% }
+  .attributes p { margin-left: 16px; font-style: italic }
+  </style>
+  <details>
+    <summary>
+      <span>
+        <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code>
+        <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
+      </span>
+    </summary>
+    <div class="attributes">
+      <h4><span>Attributes</span></h4>
+      <slot name="attributes"><p>None</p></slot>
+    </div>
+  </details>
+  <hr>
+</template>
+
+ +

この {{HTMLElement("template")}} 要素にはいくつかの機能があります。

+ + + +

<template> から <element-details> 要素を作る

+ +

次に <element-details> と名付けた新しいカスタム要素を作りましょう。 上で確認した簡単な例と同様に、{{DOMXref("Element.attachShadow")}} を利用してカスタム要素に shadow root を追加します。

+ +
customElements.define('element-details',
+  class extends HTMLElement {
+    constructor() {
+      super();
+      var template = document
+        .getElementById('element-details-template')
+        .content;
+      const shadowRoot = this.attachShadow({mode: 'open'})
+        .appendChild(template.cloneNode(true));
+  }
+})
+
+ +

名前付きスロットと共に <element-details> 要素を使う

+ +

では <element-details> 要素を実際に使ってみましょう。 

+ +
<element-details>
+  <span slot="element-name">slot</span>
+  <span slot="description">A placeholder inside a web
+    component that users can fill with their own markup,
+    with the effect of composing different DOM trees
+    together.</span>
+  <dl slot="attributes">
+    <dt>name</dt>
+    <dd>The name of the slot.</dd>
+  </dl>
+</element-details>
+
+<element-details>
+  <span slot="element-name">template</span>
+  <span slot="description">A mechanism for holding client-
+    side content that is not to be rendered when a page is
+    loaded but may subsequently be instantiated during
+    runtime using JavaScript.</span>
+</element-details>
+
+ +

このコードについて以下の点に注意してください。

+ + + +

スタイルを追加する

+ +

最後にもう少しCSSスタイルを追加します。これは、1個目の <element-details> の中で使われている {{HTMLElement("dl")}}、{{HTMLElement("dt")}}、{{HTMLElement("dd")}} 要素のために用意されています。 

+ +
  dl { margin-left: 6px; }
+  dt { font-weight: bold; color: #217ac0; font-size: 110% }
+  dt { font-family: Consolas, "Liberation Mono", Courier }
+  dd { margin-left: 16px }
+
+ + + +

結果

+ +

以上のコードを繋げてどのような結果がレンダリングされるかを確認しましょう。

+ +

{{ EmbedLiveSample('full_example', '300','400','https://mdn.mozillademos.org/files/14553/element-details.png','') }}

+ +

以下のことに着目してください。

+ + + + -- cgit v1.2.3-54-g00ecf