前言
在初涉前端之時,我就一直在好奇一個問題,為什么像:
<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 代碼,同時也可以讓開發人員創建類似 <video>
這樣的自定義標簽。
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 的基本概念就先介紹這么多,接下來將介紹它的操作方法。
~
~ 本文完,感謝閱讀!
~
學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!