1. 程式人生 > >神秘的 shadow-dom 淺析

神秘的 shadow-dom 淺析

sidebar target 深入 可見 -i school 例子 邊界 技術

說到 shadow-dom 可能很多人會很陌生。但是其實我們肯定碰到過,本文主要想簡單介紹下 shadow-dom。下面直接進入正文。

shadow-dom 是什麽

顧名思義, shadow-dom,直譯的話就是 影子dom ?我覺得可以理解為潛藏在黑暗中的 DOM 結構,也就是我們無法直接控制操縱的 DOM 結構。前端同學經常用開發者工具的話,查看 DOM 結構的時候,肯定看到過下面這樣的結構:

技術分享圖片

這裏的 #shadow-root 所包含的內容其實就是所謂的 shadow-dom

shadow-dom 其實是瀏覽器的一種能力,它允許在瀏覽器渲染文檔(document)的時候向其中的 Dom 結構中插入一棵 DOM 元素子樹,但是特殊的是,這棵子樹(shadow-dom)並不在主 DOM 樹中。

舉個栗子,也是最常見的例子, <video> 標簽,我們創建在頁面上創建一個空白的 video 標簽:

1 <video id=‘test‘></video>

查看 DOM 結構如下:

技術分享圖片

雖然我們創建的是一個空標簽,但是在這個空標簽內部,存在一個 shadow-dom ,點開 shadow-dom 可以看到內有乾坤,大有內容。其實這內部的具體內容,就是 <video> 的具體實現。

shadow-dom 結構示意

再用一幅圖總結一下:

技術分享圖片

document

這個很好理解,就是我們的正常文檔 document 。

shadow host

對於一個內部有 shadow-dom 的元素而言,它必然需要一個宿主元素,對於上面的例子而言, <video> 標簽,就是 shadow-dom 的宿主元素。

shadow-root

通過 createShadowRoot(下文會提及) 返回的文檔片段被稱為 shadow-root 。它和它的後代元素,都將對用戶隱藏,但是它們是實際存在的,在 chrome 中,我們可以通常審查元素去查看它們的具體 DOM 實現。

<video> 中,例如暫停,播放,音量控制,全屏按鈕,進度條等都是 shadow-root 的後代。它們工作時會顯示在屏幕上,但他們的 DOM 結構對用戶是不可見的。

contents

就是上述所說的 <video> 中各子組件的 DOM 的具體實現。

為什麽需要 shadow-dom

為什麽需要有這種結構呢?

Shadow-dom 是遊離在 DOM 樹之外的節點樹,但是他的創建基於普通 DOM 元素(非 document),並且創建後的 Shadow-dom 節點可以從界面上直觀的看到。更重要的是,Shadow-dom 具有良好的密封性。

這是瀏覽器提供的一種“封裝”功能,提供了一種強大的技術去隱藏一些實現細節。什麽意思呢?以 w3c 上的一個 <video> 例子為例,我們僅僅是填寫了一個空白的標簽,再加上 src 屬性裏填上視頻地址,就可以播放視頻了:

技術分享圖片

我們僅僅填寫了一行代碼,卻擁有比這行代碼更多的功能,譬如暫停,播放,音量控制,全屏按鈕,進度條等等。

這些功能具體的 DOM 實現,其實都在 shadow-dom 中:

技術分享圖片

瀏覽器的開發者們意識到作為前端開發者,引用一個 <video> 標簽的時候,每次還要寫入一大堆 DOM 去控制控件的表現和行為,既不簡潔也很困難。所以他們界定了這樣一個界限,界定了哪些是你可以訪問的,哪些實現細節是訪問不到的。

那些不希望我們訪問到的細節,則封裝在了 shadow-dom 中。然而,瀏覽器本身卻可以隨意跨越這個邊界。設置這樣一個邊界之後,瀏覽器的開發者們就可以在我們看不見的地方使用熟悉的web技術、同樣的HTML元素去創建更多的功能,而不是像我們一樣要在頁面上用div和span來堆砌這些元素。

如何控制 shadow-dom

既然是瀏覽器開發者有意隱藏起來的 DOM 結構,那麽我們是否可以控制內部的 DOM 結構呢?並非完全不可以,還是有一些方法使得我們可以控制 shadow-dom 內的一些表現。

使用偽元素控制 shadow-dom 樣式

這裏我們要使用到偽元素,通過偽元素,我們可以控制 shadow-dom 中 DOM 結構的樣式。

在 chrome 下,查看 shadow-dom 結構(如果無法看到shadow-dom需要手動打開),可以看到每個結點都加上了一個 pesudo 屬性:

技術分享圖片

有了這些屬性,我們可以通過偽元素的方式控制他們,譬如在一些場景下 video 標簽的控制條不會自動隱藏或自動顯示,可以通過偽元素指定默認顯隱方式:

如果你在 chrome 瀏覽器下閱讀本文,從上面的 codePen 可以看到,我使用偽元素修改了 video 控件條的底色為粉紅色 deeppink。

不幸的是,上面的控制方式只適用於 chrome 瀏覽器,雖然大部分現代瀏覽器已經支持 shadow-dom ,但是能夠審查 shadow-dom 內部 DOM 元素的只有 chrome 瀏覽器,其他瀏覽器仍會把這些細節隱藏。

使用 Javascript 創建一個 shadow-dom 元素

我們也可以通常 Javascript 創建 shadow-dom ,實現各類功能的封裝,主要通過:

1 2 3 4 HTMLElement.prototype.createShadowRoot = HTMLElement.prototype.createShadowRoot || HTMLElement.prototype.webkitCreateShadowRoot || function() {};

看看下面這個例子,在chrome內核瀏覽器下,將創建一個簡單的 shadow-dom ,將我們的代碼放入一個template 中,再通過 importNode 插入到 shadow-dom 中:

如果你現在在 chrome 內核瀏覽器下訪問本文,那麽上述的 codePen 中你應該可以看到 createShadowDomByJs 這一行文字,打開審查元素,會看到 <p> 結構是隱藏在 shadow-dom 中的。

shadow-dom 兼容性

技術分享圖片

shadow-dom 的未來

本文是非常基本的一些關於 shadow-dom 的概念,只是它的冰山一角,沒有十分深入的去研究。

現行的組件都是開放式的,即最終生成的 HTML DOM 結構難以與組件外部的 DOM 進行有效結構區分,樣式容易互相混淆。Shadow-dom 的 封裝隱藏性為我們提供了解決這些問題的方法。在 Web 組件化的規範中也可以看到 Shadow-dom 的身影,使用具有良好密封性的 Shadow-dom 開發下一代 Web 組件將會是一種趨勢。

更多資源及參考文章

如果你讀完本文後仍然意猶未盡,可以看看下面這些文章:

  • Introduction to Shadow DOM
  • A Guide to Web Components
  • Shadow DOM 201
  • [譯]什麽是Shadow Dom?

神秘的 shadow-dom 淺析