简介
Web 组件是一套尖端标准,具有以下特点:
- 使构建 widget 成为可能
- ...这些特征可以可靠地重复使用
- ...并且如果下一版本的组件 更改内部实现详情。
这是否意味着您必须决定何时使用 HTML/JavaScript,并且 何时使用 Web 组件?否!HTML 和 JavaScript 和互动的视觉元素微件是交互式可视化内容。它 您应该充分利用自己在 HTML 和 JavaScript 方面的技能, 开发 widget。Web 组件标准旨在帮助 你就是这么做的。
但有一个根本问题, HTML 和 JavaScript 很难使用: 封装在一起。这种封装的缺乏 意味着您的文档样式表可能会意外应用于 在 widget 内;您的 JavaScript 可能会意外修改某些部分, 在 widget 内;您的 ID 可能与微件内的 ID 重叠; 依此类推。
Web 组件由三部分组成:
Shadow DOM 可解决 DOM 树封装问题。通过 Web 组件的四个部分旨在协同工作,但是 还可以挑选要使用的 Web 组件部分。这个 本教程介绍了如何使用 Shadow DOM。
你好,影世界
有了 Shadow DOM,元素可获取与 。这种新型节点称为“影子根”。具有与之关联的影子根的元素称为“阴影” 主机。影子宿主的内容不会渲染;内容 系统会改为渲染影子根。
例如,如果您有如下标记:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
而不是
<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
Array.prototype.forEach.call(
document.querySelectorAll(selector),
function (node) { node.parentNode.removeChild(node); });
}
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1a');
document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>
您的网页看起来
<button id="ex1b">Hello, world!</button>
<script>
(function () {
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1b');
document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
return;
}
var host = document.querySelector('#ex1b');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
})();
</script>
不仅如此,当网页上的 JavaScript 询问按钮是什么时,
textContent
是,它不会获得
“こんんち关键字定位、电影流行语!”,但使用了“Hello, world!”因为 DOM 子树
在影子根下被封装了。
将内容与呈现分离
现在,我们将了解如何使用 Shadow DOM 将内容与 演示文稿。假设我们有以下名称标签:
<style>
.ex2a.outer {
border: 2px solid brown;
border-radius: 1em;
background: red;
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
}
.ex2a .boilerplate {
color: white;
font-family: sans-serif;
padding: 0.5em;
}
.ex2a .name {
color: black;
background: white;
font-family: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
以下是标记。这就是您今天要撰写的内容。没有 使用 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: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
由于 DOM 树没有封装,因此整个 DOM 结构的 名称标签会公开给文档如果网页上的其他元素 无意中使用相同的类名称进行样式设置或脚本, 会很糟糕。
我们可以避免麻烦。
第 1 步:隐藏演示文稿详情
从语义上讲,我们可能只关心:
- 它是名称标签。
- 名字是“Bob”。
首先,我们编写更接近我们想要的真实语义的标记:
<div id="nameTag">Bob</div>
然后,我们将用于呈现的所有样式和 div
<template>
元素:
<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
border: 2px solid brown;
… same as above …
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div></span>
</template>
此时,呈现的唯一内容是“Bob”。因为我们
将演示性 DOM 元素移到了内部
<template>
元素时,此类元素不会呈现
它们可以通过 JavaScript 访问。现在我们这样做
填充影子根:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
现在我们已经设置了影子根,名称标签将渲染 。如果您右键点击名称标签并检查 元素,您会发现它是贴心的语义标记:
<div id="nameTag">Bob</div>
这说明,通过使用 Shadow DOM,我们已经隐藏了 文档中名称标签的演示文稿详细信息。通过 展示细节封装在 Shadow DOM 中。
第 2 步:将内容与演示文稿分开
现在,名称标签会隐藏页面中演示文稿的详细信息 它实际上并没有将展示与内容分离开来 内容(名称“Bob”)在网页中,呈现的名称是 是我们复制到影子根中的路径。如果想要更改 我们需要在两个位置进行添加, 不同步。
HTML 元素是构成元素,您可以将按钮放入表格, 示例。在这里,我们需要“组合”:名称标签必须是 由红色背景组成的“Hi!”文本和 标记上。
作为组件的作者,您可以定义组合如何与
widget。<content>
这个
在 widget 的呈现过程中创建一个插入点,而
插入点从影子宿主中择优挑选内容进行展示
。
如果我们将 Shadow DOM 中的标记更改为以下内容:
<span class="unchanged"><template id="nameTagTemplate">
<style>
…
</style></span>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
<content></content>
</div>
</div>
<span class="unchanged"></template></span>
渲染名称标签时,影子主机的内容
<content>
元素的
。
现在,文档的结构更简单,因为名称只有 文档。如果您的网页需要更新 可以输入:
document.querySelector('#nameTag').textContent = 'Shellie';
就是这么简单。名称标签的呈现方式会自动更新
因为我们是通过投影
名称标签替换为 <content>
。
<div id="ex2b">
现在,我们已经实现内容和呈现的分离。 文档内容;呈现在 Shadow DOM 中。 当需要更新时,浏览器会自动保持同步 才能呈现内容
第 3 步:利润
通过将内容和展示方式分离开来,我们可以简化
对内容进行操纵的代码。在名称标签示例中,
只需要处理一个简单的结构,
一个 <div>
,而不是多个。
现在,如果我们更改了演示方式,就无需更改 代码!
例如,假设我们想要本地化名称标签。它仍然是一个名称 标记中,因此文档中的语义内容不会发生变化:
<div id="nameTag">Bob</div>
影子根设置代码保持不变。只不过会加入 影子根更改:
<template id="nameTagTemplate">
<style>
.outer {
border: 2px solid pink;
border-radius: 1em;
background: url(sakura.jpg);
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
font-family: sans-serif;
font-weight: bold;
}
.name {
font-size: 45pt;
font-weight: normal;
margin-top: 0.8em;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="name">
<content></content>
</div>
と申します。
</div>
</template>
这是相对于当今网络状况的一大改进 名称更新代码可能取决于 组件,简单且一致。您的姓名 更新代码无需知道 呈现方式。如果我们考虑呈现的内容,其名称 英语的“Hi!“My name is”),但首先使用的是日语。 (在“CANNOT TRANSLATE”之前)。这种区分在语义上没有意义 从更新当前显示的名称的角度开始, 因此名称更新代码不必知道这些细节。
额外学分:高级预测
在上述示例中,<content>
元素
从影子宿主中择优挑选所有内容。使用
select
属性,则可以控制
进行投影。您还可以使用多种内容
元素。
例如,如果您的文档包含以下内容:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
以及使用 CSS 选择器选择特定内容的影子根:
<div style="background: purple; padding: 1em;">
<div style="color: red;">
<content **select=".first"**></content>
</div>
<div style="color: yellow;">
<content **select="div"**></content>
</div>
<div style="color: blue;">
<content **select=".email">**</content>
</div>
</div>
<div class="email">
元素同时与
<content select="div">
和 <content
select=".email">
元素。小鲍的电子邮件重复了多少次
地址,是什么颜色的?
答案是小鲍的电子邮件地址只出现一次,并且显示为黄色。
原因在于,像 Shadow DOM 的黑客都知道,
构建屏幕上实际渲染内容的树就像一个
参加派对。内容元素是邀请用户,
将内容从文档导入后台 Shadow DOM 渲染
。这些邀请会按顺序发出;谁会得到
取决于邀请的收件人(即
select
属性。)内容,一次
被邀请、始终接受(谁不想?!)并关闭邀请
。如果系统再次向该地址发送邀请,
没有人在家,也不会来参加派对。
在上面的示例中,<div class="email">
与
div
选择器和 .email
选择器,但由于包含 div
的内容元素
选择器。
<div class="email">
进入黄色派对,
现在没人参加蓝队了(
为什么它如此蓝,虽然苦难爱着陪伴,所以你
永远不会知道。)
没有任何派对邀请的内容不会获得 完全呈现在屏幕上这就是“Hello, world”文本 第一个示例。如果您希望实现 以完全不同的方式呈现: 文档,即网页中的脚本可以访问的内容, 并将其连接到一个完全不同的 使用 JavaScript 在 Shadow DOM 中渲染模型。
例如,HTML 具有一个不错的日期选择器。如果您输入“<input
type="date">
”,则会看到整洁的弹出式日历。但如果你
想要让用户为他们的甜点选择日期范围
海岛度假(您知道...还有用红色藤蔓制成的吊床。)您
以这种方式设置文档:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
但需要创建 Shadow DOM,使用表格创建简洁日历 用于突出显示日期范围等等当用户点击 日历中的日期,该组件会在 startDate 和 endDate 输入;当用户提交表单时, 就会提交这些输入元素中的值。
如果我不想在文档中添加标签 ?原因在于,如果用户使用浏览器查看 不支持 Shadow DOM,表单仍可使用,只是 漂亮。用户会看到如下内容:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
您已通过 Shadow DOM 101
以上就是 Shadow DOM 的基础知识 - 您将通过 Shadow DOM 101 完成这一步!您可以 对 Shadow DOM 执行更多操作,例如,您可以在 一个影子主机,或者用于封装的嵌套阴影,或者架构师 使用模型驱动的视图 (MDV) 和 Shadow DOM。和 Web 组件不仅仅是 Shadow DOM。
我们将在后续博文中说明这一点。