接觸到 Web Component 才第一次聽過的名詞,花了一些時間才真正理解用法。
就請各位看下去吧!
影子 DOM(Shadow DOM)允許你將一個 DOM 樹附加到一個元素上,並且使該樹的內部對於在頁面中運行的 JavaScript 和 CSS 是隱藏的。 --MDN
一般的 DOM 就是 HTML 嗎?
其實 HTML
本身沒有太大的用處,但是瀏覽器將靜態的 HTML 解析成資料模型(物件、節點),而這些節點包含了 屬性
、方法
,讓我們可以透過 JS 來訪問這些節點,進行存取、新增、刪除或更改內容。
而以上提到的內容指的就是一般的 DOM。
Shadow DOM
是附著在某個元素上的 獨立 DOM 樹
,是一種解決一般 DOM 中缺少的 樹封裝
方法。
其實在開發過程中我們常常會遇到需要寫 !important 來覆蓋某些衝突的樣式,而使用 Shadow DOM API,可以透過 封裝 DOM 樹
的機制來解決 CSS 和 JavaScript 的衝突問題,裡面的結構與樣式不會影響到外部,也不會受到外部影響。
(圖片中的用語 light DOM
其實就是一般的 DOM,主要用來跟 shadow DOM 做區分)
好處:
壞處:
要建立 Shadow DOM,需要用 Element.attachShadow()
的方法將 shadow root 附加到元素上,而 Element 就是 Shadow host。
Element.attachShadow({ mode: "open" | "closed" })
注意用法:
<video>
, <img>
...)已有內建 shadow DOM,不允許再疊加。<table>
、<html>
...) 已有內建行為,不可作為 Shadow DOM。自定義元素
與部分安全、結構簡單的元素(div, span, p, section...)。const div = document.createElement('div');
const shadowRoot = header.attachShadow({mode: 'open'}); // 合法的用法
試試看:嘗試將 shadow DOM 附加到不合法的元素將會導致 NotSupportedError
錯誤
document.createElement('input').attachShadow({mode: 'open'}); // 非法的用法
// Uncaught NotSupportedError
若要在 Chrome 中顯示此類元素的 Shadow root,可以在 DevTools 設定,然後在 elements
部分下方選取 Show user agent shadow DOM
參考連結:https://dom.spec.whatwg.org/#shadow-trees
前一篇我們利用基本的 customElements.define
建立了一個自定元件 cat-spinner
,
接下來要加入 Shadow DOM,先建立一個 shadowRoot 變數。
class CatSpinner extends HTMLElement {
constructor() {
super();
// 還記得 this 吧?不記得要回去讀一下基礎的 JS 囉!
// 先建立一個 shadowRoot 變數
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
.spinner {
width: 40px;
height: 40px;
border: 4px solid #ddd;
border-top-color: #333;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
<div class="spinner"></div>
`;
}
}
customElements.define('cat-spinner', CatSpinner);
打開 Dev Tool 看一下:
你會發現,cat-spinner 作為 Shadow host,加入了 Shadow root 在裡面。
open 模式:
element.shadowRoot
屬性獲取 Shadow Root,並且取得內部節點。closed 模式:
null
const element = document.querySelector('cat-spinner');
console.log('open mode', element.shadowRoot) // 會得到 #shadow-root (open)
console.log('closed mode', element.shadowRoot) // 會得到 null
關於 Shadow DOM 的介紹就到這裡啦ヾ(´∀ ˋ)ノ