使用Shadow DOM創(chuàng)建Web組件
本文概述
Web Components(組件)標(biāo)準(zhǔn)是一系列***推出的標(biāo)準(zhǔn),它可以被用來(lái)創(chuàng)建可被復(fù)用的Web部件,當(dāng)頁(yè)面中所使用的Web部件被更新為新版本時(shí)不必修改 頁(yè)面中其他任何代碼。這里所說(shuō)的部件,是一種可實(shí)現(xiàn)與用戶之間的交互的可視化組件,開發(fā)者可以使用HTML代碼與JavaScript腳本代碼來(lái)開發(fā)這些 部件。Web Componnts標(biāo)準(zhǔn)定義如何開發(fā)這些部件。
目前為止,由于一些基本問(wèn)題,導(dǎo)致使用HTML代碼與JavaScript腳本代碼開發(fā)出來(lái)的部件很難被應(yīng)用在頁(yè)面中,這些問(wèn)題包括:一個(gè)部 件內(nèi)的DOM樹并沒(méi)有被封裝,這意味著你的樣式表中的樣式可能被意外地被應(yīng)用到部件中,你的JavaScript腳本代碼可能會(huì)修改部件中的某個(gè)部分,你 定義的ID可能會(huì)與部件內(nèi)部所使用的ID相同等等。
最糟糕的是,由于部件沒(méi)有被封裝,如果你更新了部件,更改了其中的內(nèi)部細(xì)節(jié),你的頁(yè)面上的樣式表及JavaScript腳本代碼可能會(huì)導(dǎo)致意想不到的結(jié)果。
一個(gè)Web組件通常由四個(gè)部分組成:模板、Shadow DOM、自定義元素與打包,其中Shadow DOM解決了組件在頁(yè)面中的封裝問(wèn)題??梢越Y(jié)合使用這四個(gè)部分,也可以單獨(dú)使用其中的一兩個(gè)部分。本文介紹如何使用Shadom DOM。目前為止只有Chrome 25瀏覽器支持Shadow DOM,且使用時(shí)必須書寫webkit前綴。
簡(jiǎn)單示例程序
通過(guò)Shadow DOM的使用,元素可以擁有一種新的被稱為shadow root的節(jié)點(diǎn),這時(shí)該元素被稱為shadow容器。瀏覽器中不會(huì)渲染shadow容器中原有內(nèi)容,而是渲染shadow root節(jié)點(diǎn)中的內(nèi)容。
例如,你可以將HTML頁(yè)面書寫為如下所示:
- <button>click me</button>
- <script>
- var host = document.querySelector('button');
- var root = host.webkitCreateShadowRoot();
- root.textContent = '點(diǎn)擊我';
- </script>
通過(guò)這段代碼,按鈕中原有文字“click me”將被替換為“點(diǎn)擊我”。請(qǐng)注意,在JavaScript腳本代碼中,按鈕的textContent屬性值仍然為“click me”,而不是“點(diǎn)擊我”,因?yàn)樵贒OM樹中shadow root節(jié)點(diǎn)是被忽視的。
一個(gè)容易被違反的規(guī)則是:你不應(yīng)該將頁(yè)面內(nèi)容安排在shadow root節(jié)點(diǎn)中。頁(yè)面內(nèi)容必須是屏幕閱讀器、搜索引擎、瀏覽器擴(kuò)展所能訪問(wèn)到的內(nèi)容。Shadow DOM從語(yǔ)義上來(lái)說(shuō)是沒(méi)有任何意義的,它只被用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)Web組件,而該Web組件中的任何內(nèi)容也能被顯示在頁(yè)面中。當(dāng)然,我們不是被強(qiáng)制使用該方 法來(lái)創(chuàng)建Web組件。
分離內(nèi)容與展示
接下來(lái),我們來(lái)看如何使用Shadow DOM將內(nèi)容與展示進(jìn)行分離。我們具有如下圖所示的一個(gè)Web組件。
其樣式代碼與HTML頁(yè)面代碼如下所示(不使用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">
- 你好,歡迎來(lái)到
- </div>
- <div class="name">
- HTML 5在線
- </div>
- </div>
因?yàn)檫@個(gè)Web組件沒(méi)有被封裝,其樣式代碼與HTML代碼是被直接書寫在樣式代碼與頁(yè)面HTML代碼中的,所以只要有人在其他地方不小心修改或重定義了該Web組件所使用的樣式類代碼,該組件就被破壞了。我們需要避免這種情況。
***步:隱藏展示細(xì)節(jié)
在這段代碼中,我們可能已經(jīng)注意到:其中有一個(gè)樣式類名為name的div元素,其中顯示“HTML 5在線”文字。首先,我們將該元素的HTML代碼修改為如下所示:
- <div id="nameComponent">HTML 5在線</div>
然后,我們將所有該Web部件用樣式代碼與HTML代碼書寫到一個(gè)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">
- 你好,歡迎來(lái)到
- </div>
- <div class="name">
- HTML 5在線
- </div>
- </div>
- </template>
現(xiàn)在,該Web組件在頁(yè)面上不可見(jiàn),因?yàn)槲覀儗⑺频搅艘粋€(gè)template元素中,我們可以在JavaScript腳本代碼中訪問(wèn)該Web組件?,F(xiàn)在,我們將它放入nameComponent元素的shadow root節(jié)點(diǎn)中,代碼如下所示:
- <script>
- var shadow = document.querySelector('#nameComponent').webkitCreateShadowRoot();
- var template = document.querySelector('#nameComponentTemplate');
- shadow.appendChild(template.content);
- template.remove();
- </script>
現(xiàn)在,該組件將仍然被顯示在頁(yè)面上。如果你用鼠標(biāo)右擊nameComponent元素并查看元素內(nèi)容,你將只能看見(jiàn)如下所示的內(nèi)容:
- <div id="nameComponent">HTML 5在線</div>
由此證明,通過(guò)Shadow DOM的使用,我們可以隱藏Web組件的展示細(xì)節(jié),因?yàn)樵摷?xì)節(jié)被封裝在元素的shadow root節(jié)點(diǎn)中。
第二步:分離內(nèi)容與展示
現(xiàn)在我們的Web組件的展示細(xì)節(jié)已經(jīng)被隱藏起來(lái)了,但Web組件中的內(nèi)容并沒(méi)有被獨(dú)立出來(lái),因?yàn)楸M管組件內(nèi)容(“HTML 5在線”)被顯示在了頁(yè)面上,但是該內(nèi)容是通過(guò)被復(fù)制在元素的shadow root節(jié)點(diǎn)中的方法顯示出來(lái)的。如果我們要修改組件內(nèi)容,我們要再一次將其復(fù)制到元素的shadow root節(jié)點(diǎn)中。
在HTML 中,元素是可組合的,例如你可以在一個(gè)table元素中放入一個(gè)按鈕。此處我們要實(shí)現(xiàn)的就是這種組合:在背景色為紅色的容器元素中放入一個(gè)“HTML 5在線”文字。
你可以通過(guò)一個(gè)新的被稱為content的元素來(lái)自定義你的Web組件中的部分內(nèi)容。該元素在Web組件中創(chuàng)建一個(gè)注入點(diǎn),在JavaScript腳本代碼中可以向該注入點(diǎn)中動(dòng)態(tài)注入內(nèi)容。
接下來(lái),我們首先修改template元素中的代碼如下所示:
- <template id="nameComponentTemplate">
- <style>
- ...
- </style>
- <div class="outer">
- <div class="boilerplate">
- 你好,歡迎來(lái)到
- </div>
- <div class="name">
- <content></content>
- </div>
- </div>
- </template>
現(xiàn)在我們的Web部件仍將被渲染在頁(yè)面上,但是原有“HTML 5在線”文字內(nèi)容將被動(dòng)態(tài)注入在content元素中。
如果你需要修改該文字內(nèi)容,你可以使用如下所示的代碼:
- document.querySelector('#nameComponent').textContent = '陸凌牛';
現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了內(nèi)容與展示的分離。內(nèi)容被顯示在頁(yè)面中,而在Web組件內(nèi)部實(shí)現(xiàn)內(nèi)容的展示。
將內(nèi)容與展示分離的好處
將內(nèi)容與展示分離的好處在于:我們可以很輕松地實(shí)現(xiàn)對(duì)組件內(nèi)容的控制。例如在上述示例中,如果需要修改“HTML 5在線”文字,我們只需修改shadow root節(jié)點(diǎn)中的內(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">
- 你好,歡迎來(lái)到
- </div>
- <div class="name">
- <content></content>
- </div>
- <div class="boilerplate littleFontSize"> 國(guó)內(nèi)首家在桌面瀏覽器中正式應(yīng)用HTML 5技術(shù)的技術(shù)網(wǎng)站。 </div>
- </div>
- </template>
事實(shí)上,這個(gè)好處可以說(shuō)是對(duì)目前Web技術(shù)的一個(gè)重大改善,因?yàn)槟阒恍桕P(guān)注組件內(nèi)部的實(shí)現(xiàn)代碼,而不需關(guān)注外部如何使用這個(gè)組件。 例如在上述示例中,我們可以在為中文頁(yè)面提供的組件中書寫“你好,歡迎來(lái)到”文字,而在為英文頁(yè)面提供的組件中書寫“Hello,welcome to”文字。
實(shí)現(xiàn)高級(jí)注入
在上面這個(gè)示例代碼中,可以動(dòng)態(tài)向content元素中注入任何內(nèi)容。事實(shí)上,我們可以使用多個(gè)content元素,并且通過(guò)select屬性定義每個(gè)content元素中所顯示內(nèi)容的樣式。
例如,你可以定義一個(gè)Web組件,其中的內(nèi)容如下所示:
- <div class='first'>示例文字1</div>
- <div class='second'>示例文字2</div>
- <div class='three'>示例文字3</div>
我們可以定義一個(gè)使用CSS樣式選擇器的shadow root節(jié)點(diǎn),其代碼如下所示:
- <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>
在這段代碼中,每一個(gè)div元素都與<content select="div">元素相匹配,<div class='first'>元素同時(shí)與<content select="first">元素相匹配,<div class='secong'>元素同時(shí)與<content select="second">元素相匹配,,<div class='three'>元素同時(shí)與<content select="three">元素相匹配。從運(yùn)行結(jié)果中我們可以看出,<div class='three'>元素的背景色為紫色,文字為藍(lán)色,這是因?yàn)槲覀冊(cè)诮M件內(nèi)部定義所有div元素的背景色為紫色,且內(nèi)容為< content select="first">的div元素的文字顏色為藍(lán)色的緣故。
本文小結(jié)
本文對(duì)Shadow DOM做一基礎(chǔ)介紹。你可以通過(guò)Shadow DOM實(shí)現(xiàn)更為復(fù)雜的處理。例如,你可以在一個(gè)shadow容器中實(shí)現(xiàn)多個(gè)shadow root節(jié)點(diǎn),可以在shadow root節(jié)點(diǎn)中放置shadow容器(即在Web組件中嵌套使用Web組件)。在Web組件標(biāo)準(zhǔn)中,包含除Shadow DOM之外的更多內(nèi)容。例如通過(guò)Custom Element(定制元素)的使用,你可以使用聲明的方式,而不是使用書寫腳本代碼的方式來(lái)創(chuàng)建組件。