Web Components系列之認識 Shadow DOM

語言: CN / TW / HK

前言

在初涉前端之時,我就一直在好奇一個問題,為什麼像:

  • <input/>
  • <select></select>
  • <audio></audio>
  • <video></video>
  • ……

等等這些標籤,看起來似乎很簡單,可為什麼可以展現出那麼豐富複雜的佈局?當時我給自己的解釋是:這些標籤都是系統控制渲染的。

現在想想,那麼解釋好像有點道理,但終歸沒有涉及到本質原因,專家級的解釋應該是:以上這些元素都是以元件的方式存在,所展現出來的那些佈局都是在元件內部定義好的,如果頁面引用了這些元素標籤,那它內部的佈局都會渲染在頁面上。

在介紹 Web Components 時講到,它的第二項技術規範為 Shadow DOM。通過了解 Shadow DOM 的相關知識,或許可以解開上面的疑惑。

檢視預設元件的 Shadow DOM

有人可能疑惑,既然說文章開頭列舉的那些元素是元件,那為什麼我在開發者工具中看到的結構是這樣的:

有什麼辦法可以看到各個元件內部的 DOM 結構嗎?答案是有的,步驟如下:

第一步:開啟開發者工具,點選右上角的設定圖示:

第二步:找到偏好設定-元素,然後對 “顯示使用者代理 Shadow DOM” 進行選中:

這時候,我們再檢視那些預設元件的內部結構,如下所示:

可以看到,這些預設標籤下都包含一個 “shadow root” 而在 shadow root 中包含的是具體佈局:

一個看似簡簡單單的 audio 元素,裡面的佈局還蠻複雜的。這不禁讓我想到一句話:功夫都在舞臺外。

在上面的截圖中,我們看到了 “shadow root” ,它其實是 Shadow DOM 的一部分。

Shadow DOM 的概念

在介紹概念之前,我們先來看看 “shadow” 這個單詞的中文釋義:

Shadow DOM,翻譯過來就是“影子 DOM” 。

影子當然都是藏在暗處,不容易讓人發現的,就像文章開頭提到的那些預設元素,如果不通過設定,我們表面上看到的就是簡單的一個標籤而已。

專業的解釋就是:Shadow DOM 是 HTML 的一個規範 ,它允許瀏覽器開發者封裝自己的HTML 標籤、CSS 樣式和特定的 Javascrip 程式碼,同時也可以讓開發人員建立類似 這樣的自定義標籤。

Shadow DOM 的意義

“元件化”備受追捧的原因自然是因為它獨特的魅力,我們只需要將定義好的元件通過簡單的一組標籤引入頁面,就可以得到預定好的效果,並且可以在多處使用。

而 Shadow DOM 能在 Web Components 體系中佔據重要的地位,是因為其具有良好的密封性,主要表現在:

  • 隱藏標記、樣式和行為;
  • 保持程式碼隔離,保證頁面的乾淨整潔,各元件內部程式碼互不影響;
  • 隱藏實現細節,便於使用更強大的語法或功能。

這就意味著,如果我們使用了 Shadow DOM,那就可以在它內部隨意的發揮,而不必擔心這些發揮會影響到頁面的其他部分,變相地給了開發人員極大的自由。

想想曾經小心翼翼地定義樣式、繫結事件的時光吧,懷念嗎?

Shadow DOM 結構

Shadow DOM 允許將隱藏的 DOM 樹附加到常規的 DOM 樹中——它以 Shadow root 節點為起始根節點,在這個根節點的下方,可以是任意元素,和普通的 DOM 元素一樣,借用網上的一張圖片:

下面是我根據自己的理解畫出來的:

大家根據自己喜好,看哪一張更容易理解就對著哪張看,都無所謂的。對應到實際的文件中,其結構如下:

在以上的結構圖中,我們看到了幾個陌生的名詞,包括我們在之前看到的 “shadow root”,它們都是 Shadow DOM 的術語,接下來我解釋一下它們各自的含義。

Shadow DOM 術語

Shadow host

一個常規 DOM節點,Shadow DOM 會被附加到這個節點上。

Shadow tree

Shadow DOM內部的DOM樹。

Shadow boundary

Shadow DOM 分界線。Shadow DOM 結束的地方,也是常規 DOM 開始的地方。

Shadow root

Shadow tree 的根節點。

用法

掛載 Shadow DOM

可使用 Element.attachShadow() 方法給指定的元素掛載一個Shadow DOM,並且返回對 ShadowRoot 的引用。

let hostEle = document.getElementById("myCard");
let shadowroot = hostEle.attachShadow({mode: "open"});

控制 Shadow DOM

你可以使用同樣的方式來操作 Shadow DOM,就和操作常規 DOM 一樣——例如新增子節點、設定屬性,以及為節點新增自己的樣式(例如通過 element.style 屬性),或者為整個 Shadow DOM 新增樣式(例如在<style>元素內新增樣式)。不同的是,Shadow DOM 內部的元素始終不會影響到它外部的元素(除了 :focus-within 元素內新增樣式),這為封裝提供了便利。

注意事項

如果一個元素底下已經有一個 Shadow DOM 掛載,繼續給它掛載的話,會報錯:

結束語

Shadow DOM 的主要作用就是其封裝的特性,使得各元件的內部程式碼互不干擾,提供一個安全的開發執行環境。

關於 Shadow DOM 的基本概念就先介紹這麼多,接下來將介紹它的操作方法。