エントリー
あいさつ、同僚、そしてWebコンポーネントに関する一連の記事の続きを紹介します。その最初の部分はここから入手できます。
この記事では、2018年3月1日付けのShadow DOM(シャドウDOM)バージョンの仕様に焦点を当てます。 最新のドラフト仕様は 、2018年3月8日付けです。
シャドウDOMのAPIを使用すると、シャドウツリーと呼ばれるツリー構造にマークアップを配置することでページのコンテンツをカプセル化できます。 DOMの通常の子孫。 Webコンポーネントを作成するためのすべてのAPIのコンテキストにおけるこのAPIは、コンポーネントの内部実装を隠すだけでなく、最小限の労力でスタイルをカプセル化する機会を提供します。
シャドウDOMは、多くの要素の内部実装のためにブラウザですでに使用されています。 例えば
<input type=”range”/>
コンソールで表示すると、単一の要素ではなく、シャドウDOMの通常のHTML要素のツリー構造であることがわかります。
基本的な概念
シャドウDOMの概念の重要な概念は、 シャドウツリーです。これは、ドキュメントにレンダリングされますが、DOMツリーにはレンダリングされない「サブツリー」です。 私にとって最も簡単なことは、シャドウツリーをドキュメントパーツとドキュメントフラグメントの間の何かと考えることでした。
シャドウツリーのルート要素はシャドウルートです。 これは、 .attachShadow(obj)メソッドが呼び出されたノードです。objは、 モードプロパティを含む設定オブジェクトです。シャドウDOMへのアクセスモードで、 「open」に設定できます(シャドウDOMへのアクセスは、 .shadowRootプロパティを使用するメインドキュメントの)または「closed」 (。shadowRootを介したアクセスはnullを返します)が、 この記事では 、 クローズモードに真剣に依存する意味がない理由について詳しく説明します 。 2018年3月2日付のドラフト仕様では、 委任されたフォーカスがシャドウホストからシャドウルートに委任される(true / false)かどうかを設定する設定も提供されましたが、6日後にこの概念は仕様から削除されました。
.attachShadow()メソッドは、シャドウツリーをノードにアタッチし、 ShadowRootオブジェクトを返します。
シャドウルートが属するツリーはライトツリーと呼ばれ、ちなみにライトツリーは別のシャドウツリーである場合があります。
スロット
シャドウツリーにはスロット要素が含まれる場合があり、この要素(documentFragmentの類似要素)がDOMでレンダリングされると、スロットはそのコンテンツに置き換えられます。
スロット要素の操作の特殊なケースは、 name属性で値が指定されているかどうかによって異なります。
name属性の値が設定されている場合
スロット要素の名前属性に値が割り当てられると、ドキュメントのレンダリング時のスロット要素は、スロット要素の名前属性の値に等しい値に設定されたスロット属性を持つドキュメント(ライトツリー)の要素に置き換えられます。
例として、前の記事の執筆中にコンパイルされたユーザー要素タブのコードを取り上げ、次の変更を加えました。
class TabNavigationItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({ mode: 'open' }); }
シャドウDOMが作成され、コンストラクターにアタッチされます。 これで、シャドウルートへのアクセスがthis.shadowRoot呼び出しを介して可能になります。
次のステップでは、ユーザー要素のレイアウトを変更することにより、.render()メソッドを変更しました。
render() { if(!this.ownerDocument.defaultView) return; this.shadowRoot.innerHTML = ` <a href="#${this._target}"><slot name="nav"></slot></a> `; }
マークアップがthis.shadowRootオブジェクトの.innerHTMLプロパティの値に渡されることに注意することが重要です。
マークアップで、カスタム要素を使用する場合、単一の条件でナビゲーション要素の内部コンテンツを設定できます。ネストされた要素は、シャドウDOMのスロット要素の名前属性の値と等しいスロット属性を持つ必要があります(この例では「nav」)、そうでない場合は表示されません。
<tab-nav-item class="active" target="First tab"> <h1 slot="nav">First</h1> </tab-nav-item>
TabContentItemクラスに同様の変更を加えます。
class TabContentItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({ mode: 'open' }); } render() { if(!this.ownerDocument.defaultView) return; this.shadowRoot.innerHTML = ` <div> <slot name="tab"></slot> </div> `; } }
シャドウDOMが含まれているため、コンテンツ属性を使用してタブのコンテンツを表示する必要がなくなりました。
このようなカスタム要素の使用は次のようになります。
<tab-element> <nav> <tab-nav-item class="active" target="First tab"> <h1 slot="nav">First</h1></tab-nav-item> <tab-nav-item target="Second tab"><h2 slot="nav">Second</h2></tab-nav-item> <tab-nav-item target="Third tab"><h3 slot="nav">Third</h3></tab-nav-item> </nav> <tab-content-item class='active' target="First tab"> <div slot="tab"> <h3>Hi, I'm the first</h3> <h1>TAB</h1> </div> </tab-content-item> <tab-content-item target="Second tab"> <p slot="tab">second one!</p> </tab-content-item> <tab-content-item target="Third tab"> <style slot="tab"> .test { border: 1px solid yellow; padding: 20px } </style> <div slot="tab"> <div class="test">I'm the third</div> </div> </tab-content-item> </tab-element>
この例は、コンポーネントのユーザーがナビゲーション要素内とタブコンテンツ要素内の両方で任意のマークアップを転送できることを示していますが、この場合も、そのようなマークアップの要素に対するスロットマーキングの必須の存在という形で制限がありますユーザー要素ごとに同じ値)。 私の意見では、これはスロット要素の2番目の動作が望ましい場合です。
name属性の値が設定されていない場合
スロット要素の名前が指定されていない場合、デフォルトでは空の文字列に等しくなり、そのようなスロットはデフォルトのスロットと呼ばれます 。 レンダリング時に、スロット属性を持たない要素に置き換えられます。
したがって、レンダーメソッドで指定されたマークアップから名前属性とその値を削除し、メインドキュメントのマークアップからスロット属性とその値を削除します。 したがって、ユーザー要素の2つのタグの間に示されるすべてのマークアップは、デフォルトスロットに埋め込まれます。 マークアップ分離コードはここにあります 。
スタイル
シャドウDOMの要素のスタイルタグ内で指定されたスタイルは、それらに限定されます。 外部環境からのCSSセレクターは、シャドウDOMのコンテンツには適用されず、したがって、そのスタイルは漏れません。これにより、簡単なセレクターを使用できるようになります。これにより、記述の利便性に加えて、パフォーマンスも向上します。
カスタムWebコンポーネントは、 ホストセレクターを使用して、シャドウDOMコンテキストからスタイルを設定できます。 同時に、そのようなセレクターのルールは、コンポーネントの外部スタイルによって書き換えることができます。これにより、コンポーネントを使用するときにスタイルを変更できます(使用時に必要に応じて調整します)。 A :ホスト(セレクター)を使用すると、コンポーネントは、ユーザーアクションとアプリケーションの状態を視覚化するためだけに使用されるセレクターと一致するときにホストをスタイルできます。 コンテキストによるスタイル設定も使用できます。コンポーネントの祖先の一部がセレクター(環境のスタイル設定に使用)に一致する場合にのみ、コンポーネントに一致するホストコンテキスト(セレクター )。
タブおよびデフォルトの外部スタイルの動作を決定する原因となるスタイルを削除し、シャドウDOMで非表示にすることにしました。そのために、TabNavigationItemクラスの.render()メソッドのマークアップ内にスタイルタグを挿入しました
render() { if (!this.ownerDocument.defaultView) return; this.shadowRoot.innerHTML = ` <style> :host{ padding: 10px; background-color: gray; border: 1px solid gray; } :host-context(.active) { background-color: #ccc; } a{ text-decoration: none; color: black; } </style> <a href="#${this._target}"><slot></slot></a> `; }
同様に、TabContentItemクラスの.render()メソッドの場合:
render() { if (!this.ownerDocument.defaultView) return; this.shadowRoot.innerHTML = ` <style> :host { display: none; padding: 20px; width: 100%; height: 50px; } :host-context(.active){ display: block; } </style> <div><slot></slot></div> `; }
シャドウDOMが関係する最後のタブコードはここにあります。タブは要素内でマークアップを受け入れることができ、独自のスタイルを含むことができますが、.render()メソッドは面倒になりました。 これは、テンプレートの仕様を検討するときに次回修正する予定です。
シャドウDOMから使用するために、 :: slotted(selector) pseudo-elementも使用できます。これは、セレクターに一致するネストされた上位要素の要素を選択する必要があります。 シャドウDOMの他のCSS機能については、 こちらをご覧ください 。
イベントについて
私のタブの例はこれを実証する最良の方法ではありませんが、シャドウDOMにはイベント動作機能もあります。 したがって、たとえば、ドキュメントによると、常に最も内側のシャドウルートで停止するイベントのリストがあります。中止、エラー、選択、変更、ロード、リセット、サイズ変更、スクロール、selectstartです。 クリック、dbclick、および他のほとんどすべてのマウスイベント、ホイール、ブラー、フォーカス、フォーカスイン、フォーカスアウト、キーダウン、キーアップ、すべてのドラッグイベント、およびその他のいくつかは、シャドウDOMボーダーを自由に越えます。 私はこれを使用することはできませんでしたが、この情報が役立つと思います。
サポートについて
現在、仕様の最初のバージョンのサポートは、 Chrome 、 Opera 、部分的にSafariで実装され、Firefoxで実装されています。
このシリーズの記事:
最初の記事: Webコンポーネント。 パート1:カスタム要素
3番目の記事: Webコンポーネント。 パート3:HTMLテンプレートとインポート
ご清聴ありがとうございました、厳しく判断しないでください。 よろしくTania_N