究竟什麼是Shadow DOM?

2022-08-29 12:01:44

shadow dom 是什麼?

顧名思義,shadow dom直譯的話就是影子dom,但我更願把它理解為DOM中的DOM。因為他能夠為Web元件中的 DOM和 CSS提供了封裝,實際上是在瀏覽器渲染檔案的時候會給指定的DOM結構插入編寫好的DOM元素,但是插入的Shadow DOM 會與主檔案的DOM保持分離,也就是說Shadow DOM不存在於主DOM樹上。

並且Shadow DOM封裝出來的DOM元素是獨立的,外部的設定不會影響到內部,內部的設定也不會影響外部。

如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新文章~

思考

理解完它的概念,我們再來思考一個問題:

為什麼我們用的一些標籤明明就是一個空元素,但他卻能夠渲染出各種複雜的場景?

  • input
  • video
  • audio
  • textarea
  • 等...

可能很多同學都沒想過為什麼這些標籤跟我們常用的div標籤不一樣,它們就簡單寫個標籤就能渲染出對應的樣式與功能;

或者有些同學理解成這都是底層渲染的事,我們不必關心。

很多人看到的是這樣的,但這和我們寫的沒有任何區別呀?別急,這就帶你看看他們的真實面目~

首先開啟瀏覽器控制檯的設定選項

然後再找到Preference -> Elements,把show user anent shadow dom勾上

這時候我們再來看一下此時的dom元素髮生了什麼變化

我們會發現這些標籤內部都大有乾坤,在這些標籤下面都多了一個shadow root,在它裡面才是這些標籤的真實佈局。

既然這些標籤內部都有一些子元素佈局,那麼我們能不能通過JavaScript來存取到它們呢?

const input = document.querySelector('input')
console.log(input.firstChild)  // null

很明顯,這是不可以的!

因為它為web開發者設定了一個邊界,界定了哪些是你可以存取的,哪些實現細節是存取不到的。然而,瀏覽器本身卻可以隨意跨越這個邊界。設定這樣一個邊界之後,它們就可以在你看不見的地方使用熟悉的web技術、同樣的HTML元素去建立更多的功能,而不是像你一樣要在頁面上用div和span來堆。

shadow dom 結構

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

就是因為這個特點所以我們才能看到上面那些單個空標籤就能夠渲染出各種各樣的複雜場景。

上面這張圖非常直觀的表現了shadow dom的結構以及它與真實dom的關係。

shadow host

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

shadow bounday

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

shadow tree

Shadow DOM 內部的 DOM 樹。

shadow root

Shadow tree 的根節點。

如何使用shadow dom?

建立一個shadow dom

我們可以使用attachShadow給指定元素掛載一個shadow dom,並且返回對shadow root的參照。

const shadowroot = root.attachShadow({mode: 'open'})
const template = `
  <div>前端南玖</div>
 `
shadowroot.innerHTML = template

shadow dom mode

當呼叫Element.attachShadow()方法t時,必須通過傳遞一個物件作為引數來指定shadow DOM樹的封裝模式,否則將會丟擲一個TypeError。該物件必須具有mode屬性,值為 openclosed

  • open shadow root 元素可以從 js 外部存取根節點,例如使用 Element.shadowRoot:
element.shadowRoot; // 返回一個 ShadowRoot 物件
  • closed 拒絕從 js 外部存取關閉的 shadow root 節點
element.shadowRoot; // 返回 null

瀏覽器通常用關閉的 shadow roo 來使某些元素的實現內部不可存取,而且不可從JavaScript更改。

對於一些不希望公開shadow root 的Web元件來說,封閉的shadow DOM看起來非常方便,然而在實踐中繞過封閉的shadow DOM並不難。但是完全隱藏shadow DOM所需的工作量也大大超過了它的價值。

哪些元素可以掛載shadow dom?

這裡需要注意的是並非所有html元素都可以掛載shadow dom,只有以下這些元素可以充當shadow dom的 shadow host

article aside blockquote body
div footer h1 h2
h3 h4 h5 h6
header main nav p
section span 任何帶有有效的名稱且可獨立存在的自定義元素

當我們嘗試在其它元素掛在shadow dom時,瀏覽器則會丟擲異常。

const input = document.querySelector('input')
const inputRoot = input.attachShadow({mode: 'open'})

shadow dom的特點

從前面的介紹,我們知道shadow dom是遊離在 DOM 樹之外的節點樹,但是它是基於普通 DOM 元素(非 document)建立的,並且建立後的 Shadow-dom 節點可以從介面上直觀的看到。最重要的一點是Shadow-dom 具有良好的密封性。

樣式

<style>
  .wx_name {
    color:aqua;
  }
</style>
<body>
    <div class="wx_name">我是真實dom</div>
    <div id="root"></div>

    <script>
        const shadowroot = root.attachShadow({mode: 'open'})
        const template = `
            <div class="wx_name">shadow dom - 前端南玖</div>
        `
        shadowroot.innerHTML = template
    </script>
</body>

它渲染出來是下面這樣的