Shadow DOM 101

Dominic Cooney
Dominic Cooney

簡介

網頁元件是一組最先進的標準,可用於:

  1. 能建構小工具
  2. ...因此可穩定地重複使用
  3. ...還有下一個版本元件時不會破壞頁面 變更內部實作詳細資料

這是否表示您必須決定使用 HTML/JavaScript 的時機 何時該使用網頁元件?不!HTML 和 JavaScript 能讓您 可以提供互動視覺元素小工具是互動式的視覺元素,這項服務 善用 HTML 和 JavaScript 技能 以及開發小工具Web 元件標準旨在 結果。

但其實一個根本問題就是 不易使用的 HTML 和 JavaScript:小工具內的 DOM 樹狀結構沒有 擷取自網頁的其餘部分。缺乏封裝 這意味著文件樣式表可能會意外地套用至文件的某些部分 小工具內您的 JavaScript 可能會不小心修改某些部分 小工具內您的 ID 可能會與小工具內的 ID 重疊; 依此類推

網頁元件由三個部分組成:

  1. 範本
  2. 陰影 DOM
  3. 自訂元素

Shadow DOM 解決 DOM 樹狀結構封裝問題。 Web 元件的四個部分可以相互配合運作 也可以挑選要使用網頁元件的哪些部分這個 教學課程會說明如何使用 Shadow DOM。

Shadow World 你好

透過 Shadow DOM,元素可獲得與 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件這種新型節點稱為「陰影根」。具有相關聯陰影根的元素稱為「陰影」影子主機的內容不會轉譯。內容 會改為轉譯陰影根

舉例來說,如果你使用的標記如下:

<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>

而不是

<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
  Array.prototype.forEach.call(
      document.querySelectorAll(selector),
      function (node) { node.parentNode.removeChild(node); });
}

if (!HTMLElement.prototype.createShadowRoot) {
  remove('#ex1a');
  document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>

頁面外觀

<button id="ex1b">Hello, world!</button>
<script>
(function () {
  if (!HTMLElement.prototype.createShadowRoot) {
    remove('#ex1b');
    document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
    return;
  }
  var host = document.querySelector('#ex1b');
  var root = host.createShadowRoot();
  root.textContent = 'こんにちは、影の世界!';
})();
</script>

不僅如此,如果網頁上的 JavaScript 會詢問 textContent是,其他人無法獲得 「🄖?, an 創業法!」,但是「Hello, world!」因為 DOM 子樹狀結構 就會封裝。

區隔內容與簡報

現在我們要探討如何使用 Shadow DOM 來區隔內容 簡報假設其中包含以下名稱標記:

<style>
.ex2a.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.ex2a .boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.ex2a .name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

以下是標記。以上是今天撰寫的內容。這沒有 使用 Shadow DOM:

<style>
.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

由於 DOM 樹狀結構缺少封裝,因此 名稱標籤即會公開至文件。如果網頁上有其他元素 不小心使用相同的類別名稱來設定樣式或指令碼 所以不會很順利

我們可以避免浪費時間。

步驟 1:隱藏簡報詳細資料

從語意的角度來看,我們可能只關心:

  • 其為名稱標記。
  • 名稱為「Bob」。

首先,我們要編寫更接近實際語意的標記:

<div id="nameTag">Bob</div>

接著,我們將簡報使用的所有樣式和 div 放入簡報中 <template> 元素:

<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
  border: 2px solid brown;

  … same as above …

</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div></span>
</template>

此時,系統只會顯示「Bob」。由於我們 已將呈現的 DOM 元素移到該元素內部 <template> 元素則不會顯示, 但「可以」透過 JavaScript 存取。現在 填入陰影根目錄:

<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
敬上

建立影子根目錄後,名稱標記後 可以選取「重新建立」,再次生成新的提示以滑鼠右鍵按一下名稱標記,然後檢查 元素,可看出這是一個「甜美的語義標記」:

<div id="nameTag">Bob</div>

這表示我們透過 Shadow DOM 使用 Shadow DOM 文件中名稱標記的顯示細節。 簡報詳細資料會封裝在 Shadow DOM 中。

步驟 2:區隔內容和簡報

我們的名稱標記現在會在頁面上隱藏簡報詳細資訊,但是名稱標記 並不會真的區隔簡報和內容 網頁內容 (即「Bob」) 顯示在網頁上的名稱。 就是我們複製到陰影根如要更改 就需要在兩個地方進行這項作業 注意。

HTML 元素可組成元素,您可以將按鈕放進表格內, 例如組成是我們需要的資訊: 該名稱標籤必須為 紅色背景構圖,「嗨!」可用來瞭解 位於名稱標記上

元件作者是元件的作者,能定義組合如何與 小工具 (使用名為 <content> 的新元素)。這個 會在小工具的呈現畫面中建立插入點,而 插入點從影子主機到呈現內容 生成的圖片

如果我們將 Shadow DOM 中的標記變更為以下程式碼:

<span class="unchanged"><template id="nameTagTemplate">
<style>
  …
</style></span>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    <content></content>
  </div>
</div>
<span class="unchanged"></template></span>

顯示名稱標記時,影子主機的內容會 投影到 <content> 元素所在的位置 出現。

現在文件結構更加簡單,因為名稱為 文件。如果您的網頁需要更新 您只需要輸入:

document.querySelector('#nameTag').textContent = 'Shellie';

這樣就大功告成了系統會自動更新名稱標記的顯示設定 因為我們要投影網頁內容 改為使用 <content> 標記名稱。

<div id="ex2b">

現在,我們已將內容和呈現方式分開。 內容;這是在 Shadow DOM 中分享的 需要時,瀏覽器會自動與瀏覽器保持同步 轉譯文字

步驟 3:利潤

將內容和簡報區隔開來 我們可以簡化 操控內容的程式碼 — 在名稱標記範例中, 您只需要處理包含 而非多個 <div>

現在當我們變更簡報時 也不需要變更任何變更過的 程式碼!

舉例來說,假設我們要將名稱標記本地化。這仍然是姓名 標記,因此文件中的語意內容並不會變更:

<div id="nameTag">Bob</div>

陰影根設定程式碼維持不變。只是因為 影子根變化:

<template id="nameTagTemplate">
<style>
.outer {
  border: 2px solid pink;
  border-radius: 1em;
  background: url(sakura.jpg);
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
  font-family: sans-serif;
  font-weight: bold;
}
.name {
  font-size: 45pt;
  font-weight: normal;
  margin-top: 0.8em;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="name">
    <content></content>
  </div>
  と申します。
</div>
</template>
敬上

與目前網路環境相比,這是一大改善。 名稱更新程式碼可能會取決於 元件,且一致。您的姓名 但不需要知道應用程式 轉譯程序如果我們考量轉譯內容,會顯示名稱 以英文輸入「嗨!我是」),但以日文首次顯示。 。這種差異在語意上完全沒有意義 從更新顯示名稱的觀點開始 所以名稱更新代碼 不需要知道詳細資訊

加分題:進階投影

在上述範例中,<content> 元素 從影子主機擷取所有內容。使用 select 屬性則能控制 也就是內容元素專案你也可以製作不同內容 元素。

舉例來說,如果您的文件含有以下內容:

<div id="nameTag">
  <div class="first">Bob</div>
  <div>B. Love</div>
  <div class="email">bob@</div>
</div>

以及使用 CSS 選取器選取特定內容的陰影根目錄:

<div style="background: purple; padding: 1em;">
  <div style="color: red;">
    <content **select=".first"**></content>
  </div>
  <div style="color: yellow;">
    <content **select="div"**></content>
  </div>
  <div style="color: blue;">
    <content **select=".email">**</content>
  </div>
</div>
敬上

<div class="email"> 元素會與以下兩者相符: <content select="div"><content select=".email"> 元素。小包的電子郵件地址 地址?顏色為何?

答案是 Bob 的電子郵件地址只要出現一次,該電子郵件地址會顯示為黃色。

原因在於,隨著 Shadow DOM 入侵團隊 建構畫面實際轉譯內容的樹狀結構 人數眾多Content 元素是邀請使用者 將文件內容轉到幕後的 Shadow DOM 算繪 。邀請會按順序傳送;誰會 邀請視的處理對象而定 (也就是 select 屬性)。一次內容 一律接受邀請 (誰不行!),然後關閉邀請 好極了!日後如果再次傳送邀請到該地址 沒有人在家,也不會來到派對上

在上述範例中,<div class="email"> 符合 div 選取器和 .email 因為具有 div 的內容元素 選取器程式碼 <div class="email"> 會參加黃色派對, 沒有人可以前來藍派對。(這可能會 不過,究竟是如此 )

但如果受邀參加「沒有」派對, 。這就是發生在 第一個範例如果您想達成 在邏輯上編寫語意模型 是網頁指令碼可存取的文件,但不會顯示 以便轉譯和連線至 以使用 JavaScript 在 Shadow DOM 中運作

例如,HTML 有實用的日期挑選器。如果你編寫 <input type="date"> 檔案,就會看到實用的彈出式日曆。但如果是 想讓使用者針對甜點挑選日期範圍 島嶼假期 (你知道的... 以紅色維尼斯製成的吊床)。個人中心 以這種方式設定文件:

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

但會建立使用表格的 Shadow DOM 這個選項會醒目顯示日期範圍。當使用者按一下 日曆中的天數,元件即會更新 輸入的開始日期和結束日期;使用者提交表單時 這些輸入元素中的值都會提交

我為什麼在文件中不會用到某些標籤 轉譯方式?原因是使用者透過瀏覽器檢視表單 您仍然可以使用表單,只是 漂亮。使用者看到的畫面如下:

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

您通過了 Shadow DOM 指南

這些是 Shadow DOM 的基本概念,您已通過 Shadow DOM 101!你可以 讓您對 Shadow DOM 執行更多操作 一個影子主機,或用於封裝或建築師的巢狀陰影 將網頁最佳化。網頁 元件不僅僅是 Shadow DOM,

我們會在後續文章中進一步說明。