選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
6 分鐘閱讀時間 (1153 個字)

Shadow DOM :獨立的Web組件

shadow-tre_20210329-092804_1 Shadow Dom Tree

什麼是Shadow DOM? 

因專案功能的關係,接觸到了Shadow DOM,那麼就來跟大家介紹一下什麼叫做Shadow DOM。

在了解什麼是Shadow DOM前,我們首先先來認識一下什麼叫做DOM呢?

什麼是Dom呢?

DOM 是 W3C 所制定的一個規範,獨立於平台與程式語言的標準。只要遵守 DOM 的規範實作,那就不受限於所使用的平台或語言開發,都可以透過 DOM 提供的 API 來操作 DOM 的內容、結構與樣式 。也因此我們可以很簡易的透過前端網頁程式 JavaScript 來對網內容做操控。而 document 物件是 「DOM tree」的根節點,所以當我們要存取 HTML 時,都從 document 物件開始。document 提供了網頁文件所需的通用函式可以呼叫使用。

DOM是Document Object Model的縮寫,我們稱之為「文件物件模型」。 當瀏覽器在載入一個網頁時,會把HTML(strings of text)內的各個標籤,包括文字、圖片、屬性等等都轉成資料模型 (objects/nodes),而這些物件最終會形成一個樹狀的結構化表示法,瀏覽器就是透過建立節點樹(即 DOM Tree )來保留 HTML 的層次結構。

舉例一個基本的 HTM L : 

<html>
  <head>
    <title>example</title>
  </head>
  <body>
    <H1>Hello World</h1>
  </body>
</html> 

而上述 HTML 的 DOM Tree 可參考下圖。

什麼是Shadow DOM

身為一個程式設計師,我們 知道網頁前端設計三大元素是:HTML、CSS、JavaScript。在開發前端網頁時,我們可能有踩過這種雷(或是沒踩過也可能聽過):當要使用或存取 HTML id 或是 class 時,可能因無法確定或是不小心讓它與頁面上現有名稱造成衝突,而此微小錯誤可能因此延伸出難以察覺的大問題。像是我們常使用 CSS的!important(雖然這語法很好用),但是卻可能造成樣式選擇器(style selectors)失去控制,整個頁面被強制受到此樣式的設定影響。相信大家看到這裡,應該點頭如搗蒜。因此,不論前後端程式,將程式模組化封裝起來,使之能夠重覆使用或是減少干擾,在開發上都是很重要的。接著就是 Shadow DOM 出場的時機了。

在對DOM有基本認識後,我們回到正題,什麼叫做影子 D OM 呢?

Shadow DOM 是三個Web組件(Web Component )標準之一: HTML Templates, Shadow DOM 和 Custom Elements。(註:還有個HTML Imports 但功能過時,現在已不建議使用)Web組件的一個重要屬性是「封裝」:可以將標記結構( markup structure),樣式(style)和行為(behavior )隱藏起來,並 與頁面上的其他代碼隔離,也就是說不會去干擾頁面上的其他代碼,也可以不受到頁面上其他程式碼的影響,並讓整個程式碼更簡潔,乾淨。

聽不太懂??沒關係,直接看以下例子就知道什麼是 Shadow DOM 了。

以下2個 HTML 的<input>就是 Shadow DOM ! 你有想過為什麼我們只是簡單寫input並設定Type="range"後,瀏覽器呈現網頁時會有出現讓我們拖曳的滑軸呢? 或設定Type="date",就能出現日期選單?

這就 Shadow DOM 的神奇之處。看似越是簡單-越是不簡單。

其實我們早就有接觸並使用到這些 Shadow DOM 了 ,只是不知道像是<audio>,<video>,<iframe>,各種<input>等元件原來這些元件都是 Shadow DOM 。 

<input type="range" min="0" max="100" step="1" value="50"> 
<input type="date"> 

但當我們檢視網頁原始碼時也看不出特別之處。若要讓 Shadow DOM 現出原形,需要開啟瀏覽器的show user agent shadow DOM設定。

  • Chrome設定

開啟開發人員工具(Ctrl+Shirt+I) > 點選此齒輪設定圖示 > 在左邊選單Preferences找到Elements 裡的show user agent shadow DOM並將它勾選。 

接著我們就能看到 Shadow DOM 的真面貌了。原來有這麼多的細節被 Shadow DOM 藏起來了~以下我們先注意到2個#shadow-root ,這分別是 <input type="range">& <input type="date">的Shadow DOM 開始 。

Shadow DOM 的特性

現在我們大概了解了什麼是 Shadow DOM 及 Shadow DOM 是屬於Web組件(Web Component )標準之一。由上面的範例,我們知道了一般情況下是看不到的 Shadow DOM 所隱藏的內容的,這也代表我們無法直接控制或是影響到 Shadow DOM 。

Shadow DOM 有以下幾點特性,並能為我們提供Web 開發中常見問題的解決方案:

  • Isolated DOM:可以把 Shadow DOM 視為DOM中的DOM,Shadow DOM有自己獨立的DOM樹,具有自己的HTML元素和CSS樣式和特定的javascript代碼,並與原始的DOM隔離不受影響。
  • Scoped CSS & Simplifies CSS:在 Shadow DOM 中定義的CSS作用範圍僅限 Shadow DOM 。因此,可以使用簡單的CSS選擇器,而不必擔心ID /Class Name命名衝突。
  • Composition:可以為你的 Shadow DOM 設計一個基於markup-based API。
  • Productivity:設計時可以不用再考慮全部頁面的影響性,可以專注在自己的 Shadow DOM 功能。

因為上述幾個特性,WEB開發人員也可以自行創建類似<input>、<video>、<audio>等的Custom Elements來使用。


Shadow DOM 基本介紹

接著介紹 Shadow DOM 的技術詞彙:

  • Shadow host:將 Shadow DOM 附加到一般DOM tree中的元素上-我們稱之這個被依附的DOM節點為 Shadow host。(Shadow DOM 可以附加到HTML節點上,像是div, button...等, 但也有一些不允許當成Shadow host的元素, 像是自己本身已經有自己的 Shadow DOM (<textarea>, <input>...),有些則是使用沒意義的(例如<img>)。)
  • Shadow treeShadow DOM 自己的DOM tree。
  • Shadow rootShadow DOM 的根節點。
  • Shadow boundaryShadow DOM 的邊界,是Javascript或CSS可以訪到到的邊界點。

如何建立與使用Shadow DOM

Creating the shadow root

 (以下程式範例是使用Shadow DOM v1 語法)我們可以使用Element.attachShadow()方法將 Shadow DOM tree 附加到我們指定的元素上並返回它的Shadow root。呼叫該方法時,可以設定 mode:open或是closed: 

let shadow = elementRef.attachShadow({mode: 'open'});
let shadow = elementRef.attachShadow({mode: 'closed'}); 

設定{mode: 'open'} ,代表我們可以用頁面的 JavaScript 來存取此 Shadow DOM 並對它做相關的操作,若是設成{mode: 'closed'},則不允許外部操控此 Shadow DOM ,在呼叫shadow.shadowRoot 時會取到 null ,像是我們也無法取得<input type="range">的shadowRoot。而在建立 Shadow DOM 時還有以下兩個限制:

  1. 不能針對已經含有內建 Shadow DOM 元素再附加 Shadow DOM ,像是<input>
  2. 無法對<img>元素附加 Shadow DOM 

Use shadow DOM Sample

以下範例建立2個div,並讓id="shadowDiv1"的div裡的<p>元素套用shadowrootClass,因此裡面的文字應該要顯示為紅色;而id="shadowDiv2"的div,採用附加Shadow DOM方式寫入的HTML內容,雖然也指定<p>元素套用shadowrootClass但是卻不會顯示紅色,而此可見 Shadow DOM 其隔融性。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shadow DOM</title>
    <style>
        .shadowrootClass {
            color: #f00;
        }
    </style>
</head>
<body>
<div id="shadowDiv1"><p class="shadowrootClass">Hello, Shadow DOM world!</p></div>
<div id="shadowDiv2"></div>
    <script>

        //選擇shadowDiv2當成shadow host
        var shadowHost = document.querySelector('#shadowDiv2');

        //在此節點上附件shadow root
        var shadowRoot = shadowHost.attachShadow({mode: 'open'});

        shadowRoot.innerHTML = '<p class="shadowrootClass">測試自建一個shadow DOM</p>';

    </script>
</body>
</html> 

設成{mode: 'open'},網頁呈現結果:

設成{mode: 'closed'},網頁呈現結果:因為設定為close,因此會有錯誤。

Shadow DOM v0 與v1 差異

Shadow DOM 最初的API現稱為v0,當前的API稱為v1

  • 建立語法Shadow DOM語法不同
    • v0:Element.createShadowRoot()
    • v1:Element.attachShadow({ mode: 'open' })
  • v0 允許Multiple Shadow Roots, 但v1不行
  • v0 的Shadow Root永遠是開放的, 而v1可以自行決定是否開放或關閉
  • 還有其他的差點可以參考這裡:https://hayatoito.github.io/2016/shadowdomv1/

進階運用

在創建 Web Components 時,就可以Shadow DOM 搭配 Custom Element ,來隔離自己元素的HTML,CSS和JS。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shadow DOM</title>
</head>
<body>
<div id="shadowDiv1"><p class="shadowrootClass">Hello, Shadow DOM world!</p></div>
<div id="shadowDiv2"></div>
<script>
    customElements.define('say-hello', class extends HTMLElement {
      connectedCallback() {
        const shadow = this.attachShadow({mode: 'open'});
        shadow.innerHTML = `<p>Hello, ${this.getAttribute('name')}</p>`;
      }
    });
</script>
<say-hello name="Claire"></say-hello>
</body>
</html> 

瀏覽器兼容性

並不是所有瀏覽器都有支援 Shadow DOM,我們可以看以下各家瀏覽器的支援程度:


 參考文章

以pdfplumber與regular expresseion解析pdf文字資料
[SQL] 使用 CTE 遞迴查詢 (PostgreSQL / MSSQL)

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/14, 週二

Captcha 圖像