目次
導入
前回の記事のカスタム タグでは、customElements オブジェクトを使用してネイティブ タグを拡張し、コンポーネントのスケーラビリティと再利用性を実現しました。したがって、コンポーネントの属性、構造、スタイルのカプセル化と分離を確実に行う方法がこの記事の内容です。共有コンテンツに関しては、この記事では Shadow DOM の基本的な使用法を紹介するだけでなく、前述のカスタム要素の使用シナリオも拡張します。
コンセプト
JS スコープの記事で、グローバル スコープとローカル スコープの概念について説明しましたが、グローバル スコープを適切に処理しないと、スコープ汚染や問題が発生する可能性があります。カスタム要素を使用してカスタムコンポーネントの機能を実装するだけでは、スタイルの競合、Dom の干渉、コンポーネント内のクラス名や ID の競合、複雑な再利用性などの問題が発生する可能性があり、コンポーネントの信頼性と保守性の低下につながります。現時点では、独立した Dom コンテナーが非常に重要です。
Shadow DOM (Shadow DOM) は、DOM 内の DOM として理解できます。フロントエンド コンポーネントのコアの 1 つです。DOM ツリーからタグを分離するメカニズムを提供し、カスタム タグや通常のタグのカプセル化を大幅に改善します。 、再利用性と保守性。
Shadow DOM を使用する前に、まずいくつかの固有名詞を理解します
シャドウホスト: Shadow DOM のコンテナ。通常の DOM 内の要素であり、ホスト要素と呼ぶことができます。ホスト要素は、カスタム タグ、ビデオ タグ、またはその他のカスタム要素などのカスタム Web コンポーネントにすることができます。
Shadow Tree: Shadow DOM 内の DOM ツリー。Shadow Host 内で、開発者はコンポーネントのスタイルと構造を含む独立した DOM サブツリーを定義できます。この内部 DOM ツリーが Shadow Tree です。
シャドウ ルート: シャドウ ツリーのルート ノード。Shadow Root を使用すると、Shadow Tree を Shadow Host に接続できます。シャドウ ルートは Shadow DOM のエントリ ポイントであり、Shadow Root を通じて Shadow Tree のコンテンツにアクセスできます。
Shadow Boundary: Shadow DOM の分離境界。Shadow DOM が終了すると、通常の DOM が始まります。この境界により、Shadow DOM が外部 DOM から分離され、相互の干渉が防止されます。外部 DOM はシャドウ ツリーのコンテンツに直接アクセスできませんが、シャドウ ホストにのみアクセスできます。
基本的な使い方
element.attachShadow 関数を使用して、Host タグにルート要素を作成します
<body>
<div id="host"></div>
<script>
const hostEle = document.querySelector("#host")
const treeEle = document.createElement("span")// 影子的树或后代节点
treeEle.textContent = "treeEle"
const rootEle = hostEle.attachShadow({ mode: 'open' });// 创建Shadow Root
rootEle.appendChild(treeEle)// 将树添加到root标签
</script>
</body>
AttachShadow関数
attachShadow 関数は、mode、delegatesFocus、slotAssignment の 3 つの属性を持つ ShadowRootInit 型のパラメータを渡すことができます。
モード
mode は必須の値で、外部アクセスと ShadowRoot プロパティの変更が許可されているかどうかを表します。open は開いている (許可されている) ことを意味し、closed は閉じられている (許可されていない) ことを意味します。
const hostEle = document.querySelector("#host")
const treeEle = document.createElement("span")
treeEle.textContent = "treeEle"
const rootEle = hostEle.attachShadow({ mode: 'closed' });// 不允许外部访问
rootEle.appendChild(treeEle)
console.log(hostEle.shadowRoot);// null
delegatesFocus (デリゲート フォーカス)
delegatesFocus はオプションで、カスタム要素のフォーカス パフォーマンスの問題を軽減するかどうかを示すブール値を渡します。カスタム ラベルを定義するとき、ラベルがフォーカスをサポートしていない場合があります。この場合、最初のフォーカス可能な部分がフォーカスになり、シャドウ ホストは利用可能なすべての :focus スタイルを提供します。この文をどう理解すればいいでしょうか?多くのオンライン記事を参照した後、要約を作成し、次のコードを考えました。
<body>
<div id="host"></div>
<script>
const hostEle = document.querySelector("#host")// 宿主元素
const rootEle = hostEle.attachShadow({ mode: 'open', delegatesFocus: true });// 根元素
const styleEle = document.createElement("style")// 样式元素
styleEle.textContent = `div {
width: 500px;
height: 80px;
border: 1px solid #000;
}
input:focus {
background: lightblue;
}`
// 内部的其他树标签
const treeEle = `<div>
<input type="text" />
</div>`
// 在Shadow DOM中添加全部元素
rootEle.appendChild(styleEle)
rootEle.innerHTML += treeEle
</script>
</body>
delegatesFocus が true に設定され、外側の div がクリックされると、内側の入力ボックスがフォーカスされます。入力ボックスは、フォーカスできるラベル内の要素です。
delegatesFocus が false に設定されている場合、外側の div をクリックしてもラベルはフォーカスされません。
カスタム要素 + Shadow DOM
基本的な使い方
カスタム タグと上記のナレッジ ポイントに基づいて、この 2 つを組み合わせて単純なカスタム コンポーネントを実装してみます。次のコードを考えてみましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ShadowDOM</title>
</head>
<body>
<my-custom-element></my-custom-element>
<script>
const elemName = "my-custom-element"
const ele = document.querySelector(elemName)
class MyCustomElement extends HTMLElement {
constructor() {
super();
this.initShadow()
}
// 基于当前自定义标签创建
initShadow() {
this.appendChild(this.attachShadow({
mode: "open"
}))
this.shadowRoot.innerHTML = "<div>hello world</div>"
}
}
customElements.define(elemName, MyCustomElement)
</script>
</body>
</html>
スタイルと属性の分離
次のコードについて考えてみましょう。props を通じてコンポーネントの動作とデータをカスタム ラベルに渡し、attachShadow を通じてスタイルを分離し、カスタム ラベルはプロパティと動作を分離してコンポーネント コンテナの効果を実現します。 label. 要素のシャドウ ルートであり、this.attachShadow の結果です。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ShadowDOM</title>
<style>
div {
/* 全局样式无法影响内部标签 */
border: 1px solid black;
}
</style>
</head>
<body>
<my-custom-element></my-custom-element>
<script>
const elemName = "my-custom-element"
const ele = document.querySelector(elemName)
class MyCustomElement extends HTMLElement {
styleContent = ""// 组件局部样式
treeEle = ""// 影子(树)子节点
constructor() {
super();
this.initProps()
this.initShadow()
}
// 通过props传入参数
initProps() {
const { ele, style } = this.props
this.styleContent = style
this.treeEle = ele
}
// 基于当前自定义标签创建
initShadow() {
this.appendChild(this.attachShadow({
mode: "open"
}))
const styleEle = document.createElement("style")
styleEle.textContent = this.styleContent
this.shadowRoot.appendChild(styleEle)
this.shadowRoot.innerHTML += this.treeEle
}
}
// 自定义标签参数
ele.props = {
ele: "<div>hello world</div>",
style: `
div {
width: 100px;
height: 100px;
background: lightcoral;
}
`
}
customElements.define(elemName, MyCustomElement)
console.log(document.querySelectorAll("div")); // [] 全局无法获取shadow中的标签
console.log(ele.shadowRoot.querySelectorAll("div")[0].textContent);// hello world 可以通过element获取shadow标签
</script>
</body>
</html>
上記のコードからわかるように、グローバル スタイルと要素は現在のshadowDOM内でのみアクセス可能であり、グローバル スコープとの分離関係を形成します。
最後に書きます
この記事では、Custom Elements と Shadow DOM を組み合わせてシンプルなカスタム コンポーネントを実装します。props を使用してパラメーターを渡すことで、コンポーネントの動作とデータがカスタム タグに渡され、attachShadow によってスタイルが分離され、属性のカプセル化が実現されます。構造とスタイル。コンポーネント コンテナの効果を実現するために分離します。では、カスタム コンポーネントはどのように相互に通信するのでしょうか? コンポーネントのスタイルとセレクターの使用方法は? 次回以降の連載では、これらの知識について詳しく解説していきますので、お楽しみに!
最後まで読んでいただきありがとうございます。良い記事だと思ったら高評価をお願いします。ありがとうございます。
関連コード
myCode: js に基づくいくつかの小規模なケースまたはプロジェクト - Gitee.com