我曾經看過某一篇文章是這樣說明的。如果說custom element是web component的骨架,那shadow DOM就是web component的靈魂。
使用shadow DOM只要簡單的二步,打開Shadow DOM和把節點掛在Shadow DOM之下。但真正的關鍵(或是說麻煩?)是在使用Shadow DOM之後。
通常都是使用"element.attachShadow({mode: 'open'})"這段語法來打開shadow DOM(element請換成目標節點),'mode'的意義後面的文章會提到,這裡先跳過。
class MyComponent extends HTMLElement {
constructor() {
super();
this.render = this.render.bind(this);
}
connectedCallback() {
this.attachShadow({mode: 'open'}); //讓指定的節點變成Shadow DOM,這行程式碼通常照抄就好
this.shadowRoot.appendChild(this.render()) //把節點掛在Shadow DOM之下
}
render() {
const template = document.createElement('template');
template.innerHTML = `
<div>
<div>
<slot name="slot1"></slot>
</div>
<div>
<slot></slot>
</div>
<div>
<slot name="slot2"></slot>
</div>
</div>
`;
return template.content;
}
}
shadow DOM不是只能用在custom element,也可以用在其他HTML element。但是有些HTML element本身就有自己的shdaow DOM(比方說"select"元素)、有些HTML element則是因為安全問題(比方說"a"元素),不是所有的HTML element都能打開shadow DOM。
const divNode = document.getElementsByTagName('div')[0];
divNode.attachShadow({mode: 'open'}); // 只要使用'attachShadow',就可以讓該節點可以使用shadow DOM
divNode.shadowRoot.innerHTML = '<p>hallo world!</p>'
const aNode = document.getElementsByTagName('a')[0];
a.attachShadow({mode: 'open'}); // a不能使用Shadow DOM
shadow DOM會讓該節點原本的子節點不會顯示在畫面上(但從DOM來看還是掛在該節點之下),轉而把Shadow DOM的子節點顯示在畫面上,就像是shadow DOM的子節點才是該節點的子節點。
外部的DOM無法使用正規方法獲得Shadow DOM的子節點(document.getElementById或是document.getElementsByClassName)。畢竟本來就是為了web component而設計的,有這種內外隔離的效果也不是不能想象。
const divNode = document.getElementsByTagName('div')[0];
divNode.attachShadow({mode: 'open'});
divNode.shadowRoot.innerHTML = '<p id='shadow'>hallo world!</p>'
document.getElementById('shadow') // 回傳為null 因為shadow DOM裡的節點不會出現在外面的DOM之中
Shadow DOM的子節點,使用的CSS和外面DOM使用的CSS是獨立的,互相不會影響。
Shadow DOM是為了web component才產生的技術。可以說,Shadow DOM好用的地方在模組化,不好用的地方也在模組化。