翻訳はgithubに投稿されるので、翻訳を手伝ったり、エラーを修正したりする場合は、プルリクエストを行うか、個人で書き込みます。
ステータス:
著者:
- ドミニククーニー、Google、 dominicc @ google.com
- Dimitri Glazkov、Google、 dglazkov @ chromium.org
はじめに
Web(またはWebコンポーネント)のコンポーネントモデルは4つのモジュールで構成されます。これらを一緒に使用すると、Webアプリケーション開発者は豊富な視覚機能を備えたウィジェットを作成でき、開発と再利用が容易になります。これは現在CSSのみでは不可能ですおよびJSライブラリ。
モジュール:
- テンプレート、将来使用できる非アクティブなマークアップの部分を決定します。
- デコレータを使用すると、CSSを使用してテンプレートの視覚的および動作の変更を管理できます。
- カスタム要素を使用すると、作成者はメインHTMLドキュメントで使用できる独自の要素 (これらの要素のプレゼンテーションとAPIを含む)を定義できます。
- 隠しDOM( シャドーDOM )は、DOMツリーでデコレーターとユーザー要素の表示と動作を互いに組み合わせる方法を定義します。
デコレータとカスタム要素を合わせてコンポーネントと呼びます。
パターン
<template>
要素には、後でスクリプトまたはテンプレートを使用できる他のモジュールで使用するためのマークアップが含まれます(たとえば、以下で説明する
<decorator>
および
<element>
)。
<template>
要素のコンテンツは解析されますが、非アクティブです。スクリプトは実行されず、画像は読み込まれません。
<template>
要素は表示されません。
スクリプトでは、そのような要素にはテンプレートで定義された静的DOM構造を含む特別なコンテンツプロパティがあります。
たとえば、開発者は、ドキュメント内で複数回作成されるDOM構造を定義し、必要に応じてそのインスタンスを作成する必要がある場合があります。
<template id="commentTemplate"> <div> <img src=""> <div class="comment"></div> … </div> </template> var t = document.querySelector("#commentTemplate"); // img[src] . // … someElement.appendChild(t.content.cloneNode());
静的DOMノードをドキュメントに追加すると、このDOMノードがinnerHTMLプロパティを介して受信されたかのように、ドキュメントが「ライブ」になります。
デコレータ
デコレータは、既存の要素の表示を強化または再定義するものです。 表現のすべての側面と同様に、デコレーターの動作はCSSを使用して制御されます。 ただし、マークアップを使用してプレゼンテーションの追加の側面を定義する機能は、デコレータのユニークな機能です。
<decorator>
要素には、デコレータのレンダリングに使用されるマークアップを定義する
<template>
要素が含まれます。
<decorator id="fade-to-white"> <template> <div style="position: relative;"> <style scoped> #fog { position: absolute; left: 0; bottom: 0; right: 0; height: 5em; background: linear-gradient( bottom, white 0, rgba(255, 255, 255, 0) 100); } </style> <content></content> <div id="fog"></div> </div> </template> </decorator>
<content>
要素は、デコレータ(より正確にはそのコンテンツ)を挿入する場所を示します。
デコレータは、CSS デコレータプロパティを使用して適用されます。
.poem { decorator: url(#fade-to-white); font-variant: small-caps; }
上記のデコレータとCSSは、このマークアップを強制します。
<div class="poem"> Two roads diverged in a yellow wood,<br> … </div>
+ただし、そのレンダリングは次のマークアップのようになります(簡潔にするため、ユーザーエージェントスタイルは省略されています)。
<div class="poem" style="font-variant: small-caps;"> <div style="position: relative;"> Two roads diverged in a yellow wood,<br> … <div style="position: absolute; left: 0; …"></div> </div> </div>
デコレーターが宣言されたCSSセレクターが無効になるようにドキュメントが変更された場合-通常、 decoratorプロパティを持つセレクターが要素に適用されなくなった場合、またはデコレーターを持つルールがstyle属性で変更された場合、デコレーターは適用されず、レンダリングする要素を返します初期状態..
CSS デコレータプロパティはネットワーク上の任意のリソースを指すことができますが、デコレータは定義が現在のドキュメントに読み込まれている間は適用されません。
ビューによって生成されるマークアップは、純粋にプレゼンテーションの使用に制限されています。スクリプト(組み込みのイベントハンドラーを含む)を実行することはできず、編集することはできません。
デコレータイベント
デコレータは、イベントハンドラをアタッチして、インタラクティブ機能を実装することもできます。 デコレータは一時的なものであるため、テンプレート内のノードはデコレータが要素に適用または削除されるたびに再構築されるため、テンプレート内のノードにイベントハンドラをハングさせたり、テンプレートの状態に依存することは効率的ではありません。
代わりに、デコレータはイベントハンドラをイベントコントローラに登録します。 イベントハンドラを登録するために、テンプレートには
<script>
要素が含まれています。 スクリプトは、デコレータが解析されるかドキュメントに挿入されるか、外部ドキュメントの一部としてロードされるときに1回実行されます。
図 イベントハンドラーを登録する
イベントコントローラーは、 thisの値としてスクリプトに渡されます。
<decorator id="decorator-event-demo"> <script> function h(event) { alert(event.target); } this.listen({selector: "#b", type: "click", handler: h}); </script> <template> <content></content> <button id="b">Bar</button> </template> </decorator>
lisnen関数を呼び出すと、ボタンが押されるとイベントハンドラーが起動します。
イベントコントローラーは、デコレーターがイベントハンドラーに適用されたノードで発生したイベントをリダイレクトします。
図 イベント処理と再マッピング
イベントリスナーが呼び出されると、イベントのターゲット値は、デコレーターが適用されたノードであり、テンプレートのコンテンツではありません。 たとえば、上記で指定したデコレータに次のコンテンツがある場合:
<span style="decorator: url(#decorator-event-demo);">Foo</span>
レンダリング:
フー[バー]
ボタンをクリックすると、
[object HTMLSpanElement]
メッセージが表示されます。
デコレータはマッピングを定義するため、 ターゲットプロパティをオーバーライドする必要があります。 ドキュメントの構造には影響しません。 デコレータが適用されている間、 ターゲットプロパティは適用先のノードで再定義されます。
また、スクリプトがテンプレートのコンテンツを変更した場合、
<script>
要素のtextContentを設定してもスクリプトの再実行が必要ないように、変更は無視されます。
デコレータはテンプレートを変更することはできず、エレメント上での自分の表示に影響を与えることはできません;デコレータを別のものに再定義することしかできません。
デコレータの例
デコレータを使用して詳細要素の単純なバージョンを作成する方法の例:
details { decorator: url(#details-closed); } details[open] { decorator: url(#details-open); }
<decorator id="details-closed"> <script> this.listen({ selector: "#summary", type: "click", handler: function (event) { event.currentTarget.open = true; } }); </script> <template> <a id="summary"> > <content select="summary:first-of-type"></content> </a> </template> </decorator> <decorator id="details-open"> <script> this.listen({ selector: "#summary", type: "click", handler: function (event) { event.currentTarget.open = false; } }); </script> <template> <a id="summary"> V <content select="summary:first-of-type"></content> </a> <content></content> </template> </decorator>
このために、2つのデコレータが必要でした。 1つは閉じた形式で詳細要素を表し、もう1つは開いた形式で表します。 各デコレータは、マウスクリックイベントハンドラを使用して、状態の変更を開閉します。
<element>
のselect属性については、以下で詳しく説明します。
カスタムアイテム
カスタム要素は、作成者が定義できる新しいタイプのDOM要素です。
カスタム要素は、デコレータを通じて表示のタイプを決定できます。 目的の要素に適用または削除できるデコレータとは異なり、ユーザー要素のタイプは固定されています。 ただし、カスタム要素は、デコレータの基本的な性質により、デコレータでは定義できないまったく新しい表示と動作を定義できます。
<element>
は、カスタム要素を定義します。
<element extends="button" name="x-fancybutton"> … </element>
extends属性は、機能を拡張する要素によって定義されます。 ユーザー要素の各インスタンスには、 extends属性で定義されたtagName があります。
name属性は、このマークアップに関連付けられるユーザー要素を識別します。 name属性の名前空間は、標準要素のタグ名の名前空間と同じです。したがって、衝突をなくすために、x-プレフィックスが使用されます。
ブラウザによってHTML要素の定義が異なりますが、その解釈はすべてHTMLのセマンティクスによって導かれます。
すべてのブラウザがユーザー定義要素をサポートしているわけではないため、作成者は新しいユーザー定義要素に最も近い値を持つHTML要素を拡張する必要があります。 たとえば、インタラクティブで、いくつかのアクションを実行することでクリックに応答するユーザー要素を定義する場合、ボタン(
<button>
)を展開する必要があり
<button>
。
必要なものに意味的に近いHTML要素がない場合、作成者は
<span>
などのニュートラル要素を展開する必要があり
<span>
。
提出
カスタム要素にはテンプレートを含めることができます:
<element extends="button" name="x-fancybutton"> <template> <style scoped> ::bound-element { display: transparent; } div.fancy { … } </style> <div class="fancy"> <content></content> <div id="t"></div> <div id="l"></div> <div id="b"></div> <div id="r"></div> </div> </template> </element>
ユーザー要素にテンプレートが含まれている場合、ユーザー要素デザイナーによってこのテンプレートのコピーが要素のシャドウDOMに挿入されます。
シャドウDOMについては後述します。
マークアップでカスタム要素を使用する
なぜなら ユーザー要素は既存のHTMLタグ(div、ボタン、オプションなど)を使用するため、ユーザー要素をいつ使用するかを決定する属性が必要です。 この属性はであり 、その値はユーザー要素の名前です。 例:
<element extends="button" name="x-fancybutton"> <!-- definition --> … </element> <button is="x-fancybutton" onclick="showTimeClicked(event);"> <!-- use --> Show time </button>
スクリプトでカスタム要素を使用する
標準のdocument.createElementメソッドを使用して、スクリプトからカスタム要素を作成できます。
var b = document.createElement("x-fancybutton"); alert(b.outerHTML); // will display '<button is="x-fancybutton"></button>'
また、
<element>
のconstructor属性を設定して、 ウィンドウオブジェクトにエクスポートする要素コンストラクターの名前を明示的に指定できます。 このコンストラクターを使用して、カスタム要素を作成できます。
<element extends="button" name="x-fancybutton" constructor="FancyButton"> … </element>
...
var b = new FancyButton(); document.body.appendChild(b); </code></pre>
ユーザー要素は、
<element>
内の
<script>
要素でプロトタイプに追加することにより、APIメソッドを宣言できます。
<element extends="button" name="x-fancybutton" constructor="FancyButton"> … <script> FancyButton.prototype.razzle = function () { … }; FancyButton.prototype.dazzle = function () { … }; </script> </element> … <script> var b = new FancyButton(); b.textContent = "Show time"; document.body.appendChild(b); b.addEventListener("click", function (event) { event.target.dazzle(); }); b.razzle(); </script>
単純な劣化を提供するために、
<script>
要素内のこれは、 HTMLElementElement型の親要素を指します :
<element extends="table" name="x-chart" constructor="Chart"> <script> if (this === window) // Use polyfills to emulate custom elements. // … else { // … } </script> </element>
技術的には、
<script>
要素内の
<script>
が
<element>
または
<decorator>
にネストされている場合、この呼び出しと同じように実行されます。
(function() { // code goes here. }).call(parentInstance);
ウィンドウオブジェクトのコンストラクターの名前が不明な状況では、カスタムコンポーネントの作成者はHTMLElementElementの generatedConstructorプロパティを使用できます 。
<element extends="table" name="x-chart"> <script> // … var Chart = this.generatedConstructor; Chart.prototype.shizzle = function() { /* … */ }; // … </script> </element>
既存のDOM要素にis属性を指定してカスタム要素を作成することはできません。 次のコードを実行しても何も起こりません。
var div = document.createElement("div"); div.setAttribute("is", "foo"); alert(div.is); // displays null alert(div.outerHTML); // displays <div></div>
アイテムの更新
カスタム項目宣言がロードされると、is属性がカスタム項目の名前に設定された各項目がカスタム項目に更新されます。 更新は、アイテムを削除してカスタムアイテムに置き換えるのと同じである必要があります。
各要素が置き換えられると、削除された要素で、バブリングもキャンセルもできないイベントが発生します。 ユーザー要素がロードされるまでドキュメントの残りの部分との対話を遅らせたいスクリプトは、特別なイベントをサブスクライブできます:
function showTimeClicked(event) { // event.target may be an HTMLButtonElement or a FancyButton if (!event.target.razzle) { // razzle, part of the FancyButton API, is missing // so upgrade has not happened yet event.target.addEventListener('upgrade', function (upgradeEvent) { showTime(upgradeEvent.replacement); }); return; } showTime(event.target); } function showTime(b) { // b is FancyButton }
定型化されていないコンテンツの表示を避けたい作成者は、CSSを使用して、カスタムアイテムが読み込まれるまで、置換されていない通常のアイテムの表示を変更できます。
ライフサイクル方法
ユーザー要素は、4つのライフサイクルメソッドにサブスクライブできます。
- created-コンストラクターを呼び出し、
<template>
要素からShadowRootのインスタンスを作成します。<template>
がない場合、 ShadowRootは nullに設定されます 。 - attributeChanged-要素の属性が変更されるたびに呼び出されます。 引数:名前、古い値、新しい値。
- 挿入-ユーザーコンポーネントがDOMツリーに挿入された後に呼び出されます。 このハンドラーでは、ユーザーコンポーネントのリソースを読み込むか、タイマーを開始できます。
- removed-ユーザー要素がDOMツリーから削除された後に呼び出されます。 ここでは、ユーザー要素がDOMツリーにないときに不要なタイマーを停止できます。
ハンドラーは、 これが要素を指している状態で呼び出されます。
挿入および削除されたハンドラーは、ユーザー定義の要素が挿入および削除されるたびに数回呼び出すことができます。
HTMLElementElement.lifecycleメソッドを呼び出すことにより、これらのハンドラーをサブスクライブできます 。
<element extends="time" name="x-clock"> <script> // … this.lifecycle({ inserted: function() { this.startUpdatingClock(); }, removed: function() { this.stopUpdatingClock(); } }); // … </script> </element>
カスタムアイテム拡張
HTML要素に加えて、
<element>
extends属性でユーザー要素の名前を指定することにより、ユーザー要素を拡張できます。
<element extends="x-clock" name="x-grandfatherclock"> … </element>
エラー処理
カスタム要素をレンダリングするときにエラーを処理するためのオプションがいくつかあります。
- 要素のtagNameは、ユーザー要素のextends属性の値と一致しません。たとえば、
<div is="x-fancybutton">
ですが、<element name="x-fancybutton" extends="button">
です。 この場合、 is属性は解析中に破棄されます。 - is属性の値は既存のアイテムと一致しません。 この状況は、ユーザー定義要素の定義がまだロードされていない場合と見なされ、定義がロードされるとすぐに要素が更新されます。
- extends属性値が既存の要素と一致しません。 この場合、カスタム属性の定義の処理は、 extends属性で指定された要素がロードされるまで保持されます。
- is属性の値はx-で始まっていません。 この場合、 is属性は無視されます。
- name属性の値はx-で始まっていません。 この場合、ユーザー要素の定義は無効と見なされ、無視されます。
トランスレーターから:次の部分では、WebコンポーネントでのシャドウDOMの使用、外部ユーザー要素とデコレーター、およびWebコンポーネントの分離、制限、カプセル化について見ていきます。