W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
Shadow DOM 為封裝而生。它可以讓一個(gè)組件擁有自己的「影子」DOM 樹(shù),這個(gè) DOM 樹(shù)不能在主文檔中被任意訪(fǎng)問(wèn),可能擁有局部樣式規(guī)則,還有其他特性。
你是否曾經(jīng)思考過(guò)復(fù)雜的瀏覽器控件是如何被創(chuàng)建和添加樣式的?
瀏覽器在內(nèi)部使用 DOM/CSS 來(lái)繪制它們。這個(gè) DOM 結(jié)構(gòu)一般來(lái)說(shuō)對(duì)我們是隱藏的,但我們可以在開(kāi)發(fā)者工具里面看見(jiàn)它。比如,在 Chrome 里,我們需要打開(kāi)「Show user agent shadow DOM」選項(xiàng)。
然后 <input type="range">
看起來(lái)會(huì)像這樣:
你在 #shadow-root
下看到的就是被稱(chēng)為「shadow DOM」的東西。
我們不能使用一般的 JavaScript 調(diào)用或者選擇器來(lái)獲取內(nèi)建 shadow DOM 元素。它們不是常規(guī)的子元素,而是一個(gè)強(qiáng)大的封裝手段。
在上面的例子中,我們可以看到一個(gè)有用的屬性 pseudo
。這是一個(gè)因?yàn)闅v史原因而存在的屬性,并不在標(biāo)準(zhǔn)中。我們可以使用它來(lái)給子元素加上 CSS 樣式,像這樣:
<style>
/* 讓滑塊軌道變紅 */
input::-webkit-slider-runnable-track {
background: red;
}
</style>
<input type="range">
重申一次,pseudo
是一個(gè)非標(biāo)準(zhǔn)的屬性。按照時(shí)間順序來(lái)說(shuō),瀏覽器首先實(shí)驗(yàn)了使用內(nèi)部 DOM 結(jié)構(gòu)來(lái)實(shí)現(xiàn)控件,然后,在一段時(shí)間之后,shadow DOM 才被標(biāo)準(zhǔn)化來(lái)讓我們,開(kāi)發(fā)者們,做類(lèi)似的事。
接下來(lái),我們將要使用現(xiàn)代 shadow DOM 標(biāo)準(zhǔn),它在 DOM spec 和其他相關(guān)標(biāo)準(zhǔn)中可以被找到。
一個(gè) DOM 元素可以有以下兩類(lèi) DOM 子樹(shù):
如果一個(gè)元素同時(shí)有以上兩種子樹(shù),那么瀏覽器只渲染 shadow tree。但是我們同樣可以設(shè)置兩種樹(shù)的組合。我們將會(huì)在后面的章節(jié) Shadow DOM 插槽,組成 中看到更多細(xì)節(jié)。
影子樹(shù)可以在自定義元素中被使用,其作用是隱藏組件內(nèi)部結(jié)構(gòu)和添加只在組件內(nèi)有效的樣式。
比如,這個(gè) <show-hello>
元素將它的內(nèi)部 DOM 隱藏在了影子里面:
<script>
customElements.define('show-hello', class extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `<p>
Hello, ${this.getAttribute('name')}
</p>`;
}
});
</script>
<show-hello name="John"></show-hello>
這就是在 Chrome 開(kāi)發(fā)者工具中看到的最終樣子,所有的內(nèi)容都在「#shadow-root」下:
首先,調(diào)用 elem.attachShadow({mode: …})
可以創(chuàng)建一個(gè) shadow tree。
這里有兩個(gè)限制:
elem
? 必須是自定義元素,或者是以下元素的其中一個(gè):「article」、「aside」、「blockquote」、「body」、「div」、「footer」、「h1…h(huán)6」、「header」、「main」、「nav」、「p」、「section」或者「span」。其他元素,比如 ?<img>
?,不能容納 shadow tree。mode
選項(xiàng)可以設(shè)定封裝層級(jí)。他必須是以下兩個(gè)值之一:
「open」
? —— shadow root 可以通過(guò) ?elem.shadowRoot
? 訪(fǎng)問(wèn)。任何代碼都可以訪(fǎng)問(wèn) elem
的 shadow tree。
「closed」
? —— ?elem.shadowRoot
? 永遠(yuǎn)是 ?null
?。我們只能通過(guò) attachShadow
返回的指針來(lái)訪(fǎng)問(wèn) shadow DOM(并且可能隱藏在一個(gè) class 中)。瀏覽器原生的 shadow tree,比如 <input type="range">
,是封閉的。沒(méi)有任何方法可以訪(fǎng)問(wèn)它們。
attachShadow
返回的 shadow root,和任何元素一樣:我們可以使用 innerHTML
或者 DOM 方法,比如 append
來(lái)擴(kuò)展它。
我們稱(chēng)有 shadow root 的元素叫做「shadow tree host」,可以通過(guò) shadow root 的 host
屬性訪(fǎng)問(wèn):
// 假設(shè) {mode: "open"},否則 elem.shadowRoot 是 null
alert(elem.shadowRoot.host === elem); // true
Shadow DOM 被非常明顯地和主文檔分開(kāi):
querySelector
? 不可見(jiàn)。實(shí)際上,Shadow DOM 中的元素可能與 light DOM 中某些元素的 id 沖突。這些元素必須在 shadow tree 中獨(dú)一無(wú)二。比如:
<style>
/* 文檔樣式對(duì) #elem 內(nèi)的 shadow tree 無(wú)作用 (1) */
p { color: red; }
</style>
<div id="elem"></div>
<script>
elem.attachShadow({mode: 'open'});
// shadow tree 有自己的樣式 (2)
elem.shadowRoot.innerHTML = `
<style> p { font-weight: bold; } </style>
<p>Hello, John!</p>
`;
// <p> 只對(duì) shadow tree 里面的查詢(xún)可見(jiàn) (3)
alert(document.querySelectorAll('p').length); // 0
alert(elem.shadowRoot.querySelectorAll('p').length); // 1
</script>
innerHTML
?。Shadow DOM 是創(chuàng)建組件級(jí)別 DOM 的一種方法。
shadowRoot = elem.attachShadow({mode: open|closed})
? —— 為 ?elem
? 創(chuàng)建 shadow DOM。如果 ?mode="open"
?,那么它通過(guò) ?elem.shadowRoot
? 屬性被訪(fǎng)問(wèn)。innerHTML
? 或者其他 DOM 方法來(lái)擴(kuò)展 ?shadowRoot
?。Shadow DOM 元素:
querySelector
?。Shadow DOM,如果存在的話(huà),會(huì)被瀏覽器渲染而不是所謂的 「light DOM」(普通子元素)。在 Shadow DOM 插槽,組成 章節(jié)中我們將會(huì)看到如何組織它們。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: