本文概述
Web Components(组件)标准是一系列***推出的标准,它可以被用来创建可被复用的Web部件,当页面中所使用的Web部件被更新为新版本时不必修改 页面中其他任何代码。这里所说的部件,是一种可实现与用户之间的交互的可视化组件,开发者可以使用HTML代码与JavaScript脚本代码来开发这些 部件。Web Componnts标准定义如何开发这些部件。
目前为止,由于一些基本问题,导致使用HTML代码与JavaScript脚本代码开发出来的部件很难被应用在页面中,这些问题包括:一个部 件内的DOM树并没有被封装,这意味着你的样式表中的样式可能被意外地被应用到部件中,你的JavaScript脚本代码可能会修改部件中的某个部分,你 定义的ID可能会与部件内部所使用的ID相同等等。
最糟糕的是,由于部件没有被封装,如果你更新了部件,更改了其中的内部细节,你的页面上的样式表及JavaScript脚本代码可能会导致意想不到的结果。
一个Web组件通常由四个部分组成:模板、Shadow DOM、自定义元素与打包,其中Shadow DOM解决了组件在页面中的封装问题。可以结合使用这四个部分,也可以单独使用其中的一两个部分。本文介绍如何使用Shadom DOM。目前为止只有Chrome 25浏览器支持Shadow DOM,且使用时必须书写webkit前缀。
简单示例程序
通过Shadow DOM的使用,元素可以拥有一种新的被称为shadow root的节点,这时该元素被称为shadow容器。浏览器中不会渲染shadow容器中原有内容,而是渲染shadow root节点中的内容。
例如,你可以将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节点是被忽视的。
一个容易被违反的规则是:你不应该将页面内容安排在shadow root节点中。页面内容必须是屏幕阅读器、搜索引擎、浏览器扩展所能访问到的内容。Shadow DOM从语义上来说是没有任何意义的,它只被用来动态创建一个Web组件,而该Web组件中的任何内容也能被显示在页面中。当然,我们不是被强制使用该方 法来创建Web组件。
分离内容与展示
接下来,我们来看如何使用Shadow DOM将内容与展示进行分离。我们具有如下图所示的一个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组件所使用的样式类代码,该组件就被破坏了。我们需要避免这种情况。
***步:隐藏展示细节
在这段代码中,我们可能已经注意到:其中有一个样式类名为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>
现在,该Web组件在页面上不可见,因为我们将它移到了一个template元素中,我们可以在JavaScript脚本代码中访问该Web组件。现在,我们将它放入nameComponent元素的shadow root节点中,代码如下所示:
- <script>
- var shadow = document.querySelector('#nameComponent').webkitCreateShadowRoot();
- var template = document.querySelector('#nameComponentTemplate');
- shadow.appendChild(template.content);
- template.remove();
- </script>
现在,该组件将仍然被显示在页面上。如果你用鼠标右击nameComponent元素并查看元素内容,你将只能看见如下所示的内容:
- <div id="nameComponent">HTML 5在线</div>
由此证明,通过Shadow DOM的使用,我们可以隐藏Web组件的展示细节,因为该细节被封装在元素的shadow root节点中。
第二步:分离内容与展示
现在我们的Web组件的展示细节已经被隐藏起来了,但Web组件中的内容并没有被独立出来,因为尽管组件内容(“HTML 5在线”)被显示在了页面上,但是该内容是通过被复制在元素的shadow root节点中的方法显示出来的。如果我们要修改组件内容,我们要再一次将其复制到元素的shadow root节点中。
在HTML 中,元素是可组合的,例如你可以在一个table元素中放入一个按钮。此处我们要实现的就是这种组合:在背景色为红色的容器元素中放入一个“HTML 5在线”文字。
你可以通过一个新的被称为content的元素来自定义你的Web组件中的部分内容。该元素在Web组件中创建一个注入点,在JavaScript脚本代码中可以向该注入点中动态注入内容。
接下来,我们首先修改template元素中的代码如下所示:
- <template id="nameComponentTemplate">
- <style>
- ...
- </style>
- <div class="outer">
- <div class="boilerplate">
- 你好,欢迎来到
- </div>
- <div class="name">
- <content></content>
- </div>
- </div>
- </template>
现在我们的Web部件仍将被渲染在页面上,但是原有“HTML 5在线”文字内容将被动态注入在content元素中。
如果你需要修改该文字内容,你可以使用如下所示的代码:
- document.querySelector('#nameComponent').textContent = '陆凌牛';
现在我们已经实现了内容与展示的分离。内容被显示在页面中,而在Web组件内部实现内容的展示。
将内容与展示分离的好处
将内容与展示分离的好处在于:我们可以很轻松地实现对组件内容的控制。例如在上述示例中,如果需要修改“HTML 5在线”文字,我们只需修改shadow root节点中的内容(即textContent属性值)即可,不需书写其他任何代码。
如果要修改组件中的其他任何内容或样式,我们也只需要修改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"> 国内首家在桌面浏览器中正式应用HTML 5技术的技术网站。 </div>
- </div>
- </template>
事实上,这个好处可以说是对目前Web技术的一个重大改善,因为你只需关注组件内部的实现代码,而不需关注外部如何使用这个组件。 例如在上述示例中,我们可以在为中文页面提供的组件中书写“你好,欢迎来到”文字,而在为英文页面提供的组件中书写“Hello,welcome to”文字。
实现高级注入
在上面这个示例代码中,可以动态向content元素中注入任何内容。事实上,我们可以使用多个content元素,并且通过select属性定义每个content元素中所显示内容的样式。
例如,你可以定义一个Web组件,其中的内容如下所示:
- <div class='first'>示例文字1</div>
- <div class='second'>示例文字2</div>
- <div class='three'>示例文字3</div>
我们可以定义一个使用CSS样式选择器的shadow root节点,其代码如下所示:
- <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">元素相匹配。从运行结果中我们可以看出,<div class='three'>元素的背景色为紫色,文字为蓝色,这是因为我们在组件内部定义所有div元素的背景色为紫色,且内容为< content select="first">的div元素的文字颜色为蓝色的缘故。
本文小结
本文对Shadow DOM做一基础介绍。你可以通过Shadow DOM实现更为复杂的处理。例如,你可以在一个shadow容器中实现多个shadow root节点,可以在shadow root节点中放置shadow容器(即在Web组件中嵌套使用Web组件)。在Web组件标准中,包含除Shadow DOM之外的更多内容。例如通过Custom Element(定制元素)的使用,你可以使用声明的方式,而不是使用书写脚本代码的方式来创建组件。