使用Shadow DOM創(chuàng)建Web組件
本文概述
Web Components(組件)標(biāo)準(zhǔn)是一系列***推出的標(biāo)準(zhǔn),它可以被用來創(chuàng)建可被復(fù)用的Web部件,當(dāng)頁面中所使用的Web部件被更新為新版本時不必修改 頁面中其他任何代碼。這里所說的部件,是一種可實現(xiàn)與用戶之間的交互的可視化組件,開發(fā)者可以使用HTML代碼與JavaScript腳本代碼來開發(fā)這些 部件。Web Componnts標(biāo)準(zhǔn)定義如何開發(fā)這些部件。
目前為止,由于一些基本問題,導(dǎo)致使用HTML代碼與JavaScript腳本代碼開發(fā)出來的部件很難被應(yīng)用在頁面中,這些問題包括:一個部 件內(nèi)的DOM樹并沒有被封裝,這意味著你的樣式表中的樣式可能被意外地被應(yīng)用到部件中,你的JavaScript腳本代碼可能會修改部件中的某個部分,你 定義的ID可能會與部件內(nèi)部所使用的ID相同等等。
最糟糕的是,由于部件沒有被封裝,如果你更新了部件,更改了其中的內(nèi)部細節(jié),你的頁面上的樣式表及JavaScript腳本代碼可能會導(dǎo)致意想不到的結(jié)果。
一個Web組件通常由四個部分組成:模板、Shadow DOM、自定義元素與打包,其中Shadow DOM解決了組件在頁面中的封裝問題。可以結(jié)合使用這四個部分,也可以單獨使用其中的一兩個部分。本文介紹如何使用Shadom DOM。目前為止只有Chrome 25瀏覽器支持Shadow DOM,且使用時必須書寫webkit前綴。
簡單示例程序
通過Shadow DOM的使用,元素可以擁有一種新的被稱為shadow root的節(jié)點,這時該元素被稱為shadow容器。瀏覽器中不會渲染shadow容器中原有內(nèi)容,而是渲染shadow root節(jié)點中的內(nèi)容。
例如,你可以將HTML頁面書寫為如下所示:
- <button>click me</button>
 - <script>
 - var host = document.querySelector('button');
 - var root = host.webkitCreateShadowRoot();
 - root.textContent = '點擊我';
 - </script>
 
通過這段代碼,按鈕中原有文字“click me”將被替換為“點擊我”。請注意,在JavaScript腳本代碼中,按鈕的textContent屬性值仍然為“click me”,而不是“點擊我”,因為在DOM樹中shadow root節(jié)點是被忽視的。
一個容易被違反的規(guī)則是:你不應(yīng)該將頁面內(nèi)容安排在shadow root節(jié)點中。頁面內(nèi)容必須是屏幕閱讀器、搜索引擎、瀏覽器擴展所能訪問到的內(nèi)容。Shadow DOM從語義上來說是沒有任何意義的,它只被用來動態(tài)創(chuàng)建一個Web組件,而該Web組件中的任何內(nèi)容也能被顯示在頁面中。當(dāng)然,我們不是被強制使用該方 法來創(chuàng)建Web組件。
分離內(nèi)容與展示
接下來,我們來看如何使用Shadow DOM將內(nèi)容與展示進行分離。我們具有如下圖所示的一個Web組件。

其樣式代碼與HTML頁面代碼如下所示(不使用Shadow DOM):
- <style>
 - .outer {
 - border: 2px solid brown;
 - border-radius: 1em;
 - background: red;
 - font-size: 20pt;
 - width: 12em;
 - height: 7em;
 - text-align: center;
 - }
 - .boilerplate {
 - color: white;
 - font-family: sans-serif;
 - padding: 0.5em;
 - }
 - .name {
 - color: black;
 - background: white;
 - font-family: "宋體";
 - font-size: 30pt;
 - padding-top: 0.2em;
 - }
 - </style>
 - <div class="outer">
 - <div class="boilerplate">
 - 你好,歡迎來到
 - </div>
 - <div class="name">
 - HTML 5在線
 - </div>
 - </div>
 
因為這個Web組件沒有被封裝,其樣式代碼與HTML代碼是被直接書寫在樣式代碼與頁面HTML代碼中的,所以只要有人在其他地方不小心修改或重定義了該Web組件所使用的樣式類代碼,該組件就被破壞了。我們需要避免這種情況。
***步:隱藏展示細節(jié)
在這段代碼中,我們可能已經(jīng)注意到:其中有一個樣式類名為name的div元素,其中顯示“HTML 5在線”文字。首先,我們將該元素的HTML代碼修改為如下所示:
- <div id="nameComponent">HTML 5在線</div>
 
然后,我們將所有該Web部件用樣式代碼與HTML代碼書寫到一個id為nameComponentTemplate的template元素中:
- <div id="nameComponent">HTML 5在線</div>
 - <template id="nameComponentTemplate">
 - <style>
 - .outer {
 - border: 2px solid brown;
 - border-radius: 1em;
 - background: red;
 - font-size: 20pt;
 - width: 12em;
 - height: 7em;
 - text-align: center;
 - }
 - .boilerplate {
 - color: white;
 - font-family: sans-serif;
 - padding: 0.5em;
 - }
 - .name {
 - color: black;
 - background: white;
 - font-family: "宋體";
 - font-size: 30pt;
 - padding-top: 0.2em;
 - }
 - </style>
 - <div class="outer">
 - <div class="boilerplate">
 - 你好,歡迎來到
 - </div>
 - <div class="name">
 - HTML 5在線
 - </div>
 - </div>
 - </template>
 
現(xiàn)在,該Web組件在頁面上不可見,因為我們將它移到了一個template元素中,我們可以在JavaScript腳本代碼中訪問該Web組件?,F(xiàn)在,我們將它放入nameComponent元素的shadow root節(jié)點中,代碼如下所示:
- <script>
 - var shadow = document.querySelector('#nameComponent').webkitCreateShadowRoot();
 - var template = document.querySelector('#nameComponentTemplate');
 - shadow.appendChild(template.content);
 - template.remove();
 - </script>
 
現(xiàn)在,該組件將仍然被顯示在頁面上。如果你用鼠標(biāo)右擊nameComponent元素并查看元素內(nèi)容,你將只能看見如下所示的內(nèi)容:
- <div id="nameComponent">HTML 5在線</div>
 
由此證明,通過Shadow DOM的使用,我們可以隱藏Web組件的展示細節(jié),因為該細節(jié)被封裝在元素的shadow root節(jié)點中。
第二步:分離內(nèi)容與展示
現(xiàn)在我們的Web組件的展示細節(jié)已經(jīng)被隱藏起來了,但Web組件中的內(nèi)容并沒有被獨立出來,因為盡管組件內(nèi)容(“HTML 5在線”)被顯示在了頁面上,但是該內(nèi)容是通過被復(fù)制在元素的shadow root節(jié)點中的方法顯示出來的。如果我們要修改組件內(nèi)容,我們要再一次將其復(fù)制到元素的shadow root節(jié)點中。
在HTML 中,元素是可組合的,例如你可以在一個table元素中放入一個按鈕。此處我們要實現(xiàn)的就是這種組合:在背景色為紅色的容器元素中放入一個“HTML 5在線”文字。
你可以通過一個新的被稱為content的元素來自定義你的Web組件中的部分內(nèi)容。該元素在Web組件中創(chuàng)建一個注入點,在JavaScript腳本代碼中可以向該注入點中動態(tài)注入內(nèi)容。
接下來,我們首先修改template元素中的代碼如下所示:
- <template id="nameComponentTemplate">
 - <style>
 - ...
 - </style>
 - <div class="outer">
 - <div class="boilerplate">
 - 你好,歡迎來到
 - </div>
 - <div class="name">
 - <content></content>
 - </div>
 - </div>
 - </template>
 
現(xiàn)在我們的Web部件仍將被渲染在頁面上,但是原有“HTML 5在線”文字內(nèi)容將被動態(tài)注入在content元素中。
如果你需要修改該文字內(nèi)容,你可以使用如下所示的代碼:
- document.querySelector('#nameComponent').textContent = '陸凌牛';
 
現(xiàn)在我們已經(jīng)實現(xiàn)了內(nèi)容與展示的分離。內(nèi)容被顯示在頁面中,而在Web組件內(nèi)部實現(xiàn)內(nèi)容的展示。
將內(nèi)容與展示分離的好處
將內(nèi)容與展示分離的好處在于:我們可以很輕松地實現(xiàn)對組件內(nèi)容的控制。例如在上述示例中,如果需要修改“HTML 5在線”文字,我們只需修改shadow root節(jié)點中的內(nèi)容(即textContent屬性值)即可,不需書寫其他任何代碼。
如果要修改組件中的其他任何內(nèi)容或樣式,我們也只需要修改template元素中的樣式代碼或HTML代碼即可:
- <template id="nameComponentTemplate">
 - <style>
 - .outer {
 - border: 2px solid brown;
 - border-radius: 1em;
 - background: red;
 - font-size: 20pt;
 - width: 12em;
 - height: 7em;
 - text-align: center;
 - }
 - .boilerplate {
 - color: white;
 - font-family: sans-serif;
 - padding: 0.5em;
 - }
 - .littleFontSize{ font-size: 15pt; }
 - .name {
 - color: black;
 - background: white;
 - font-family: "宋體";
 - font-size: 30pt;
 - padding-top: 0.2em;
 - }
 - </style>
 - <div class="outer">
 - <div class="boilerplate">
 - 你好,歡迎來到
 - </div>
 - <div class="name">
 - <content></content>
 - </div>
 - <div class="boilerplate littleFontSize"> 國內(nèi)首家在桌面瀏覽器中正式應(yīng)用HTML 5技術(shù)的技術(shù)網(wǎng)站。 </div>
 - </div>
 - </template>
 
事實上,這個好處可以說是對目前Web技術(shù)的一個重大改善,因為你只需關(guān)注組件內(nèi)部的實現(xiàn)代碼,而不需關(guān)注外部如何使用這個組件。 例如在上述示例中,我們可以在為中文頁面提供的組件中書寫“你好,歡迎來到”文字,而在為英文頁面提供的組件中書寫“Hello,welcome to”文字。
實現(xiàn)高級注入
在上面這個示例代碼中,可以動態(tài)向content元素中注入任何內(nèi)容。事實上,我們可以使用多個content元素,并且通過select屬性定義每個content元素中所顯示內(nèi)容的樣式。
例如,你可以定義一個Web組件,其中的內(nèi)容如下所示:
- <div class='first'>示例文字1</div>
 - <div class='second'>示例文字2</div>
 - <div class='three'>示例文字3</div>
 
我們可以定義一個使用CSS樣式選擇器的shadow root節(jié)點,其代碼如下所示:
- <template id="nameComponentTemplate">
 - <div style="background: purple; padding: 1em;">
 - <div style="color: red;">
 - <content select=".first"></content>
 - </div>
 - <div style="color: yellow;">
 - <content select=".second"></content>
 - </div>
 - <div style="color: blue;">
 - <content select=".three"></content>
 - </div>
 - </div>
 - </template>
 
在這段代碼中,每一個div元素都與<content select="div">元素相匹配,<div class='first'>元素同時與<content select="first">元素相匹配,<div class='secong'>元素同時與<content select="second">元素相匹配,,<div class='three'>元素同時與<content select="three">元素相匹配。從運行結(jié)果中我們可以看出,<div class='three'>元素的背景色為紫色,文字為藍色,這是因為我們在組件內(nèi)部定義所有div元素的背景色為紫色,且內(nèi)容為< content select="first">的div元素的文字顏色為藍色的緣故。
本文小結(jié)
本文對Shadow DOM做一基礎(chǔ)介紹。你可以通過Shadow DOM實現(xiàn)更為復(fù)雜的處理。例如,你可以在一個shadow容器中實現(xiàn)多個shadow root節(jié)點,可以在shadow root節(jié)點中放置shadow容器(即在Web組件中嵌套使用Web組件)。在Web組件標(biāo)準(zhǔn)中,包含除Shadow DOM之外的更多內(nèi)容。例如通過Custom Element(定制元素)的使用,你可以使用聲明的方式,而不是使用書寫腳本代碼的方式來創(chuàng)建組件。















 
 
 





 
 
 
 