Shadow DOM
ShadowDOm是一个HTML的新规范,其允许开发者封装自己的HTML标签、CSS样式和JavaScript代码。这些新标签和相关的API被称为WebComponents。
当HTML转化成DOM时,每个元素都会变成一个节点(node)。而一组互相嵌套的一组节点被称为节点树(node tree)。
要创建一个影子树,首先要指定一个节点担任影子宿主(shadow host),然后给影子素数添加一个称作影子根(shadow root)新节点。影子根作为影子树的第一个节点,其他的节点都是它的子节点。
一个简单的例子
<body>
<div class="widget">Hello, world!</div>
<script>
var host = document.querySelector(".widget");
var root = host.createShadowRoot();
var header = document.createElement('h1');
header.textContent = '一只野生的影子标题出现了!';
root.appendChild(header)
</script>
</body>
content标签
<body>
<div class="box">
<div class="name">付鑫</div>
<div class="sex">男</div>
<div class="hobby">电影</div>
<p>我专注前端开发(HTML/CSS/JavaScript),还会一点 Node 和 Ruby。</p>
<p>从事写作,偶尔也会写点博客。</p>
<p>尽管我是南方人,但我最近都在美丽的旧金山工作和生活。</p>
</div>
<template class="box-template">
<dl>
<dt>名字</dt>
<dd>
<content select=".name"></content>
</dd>
<dt>性别</dt>
<dd>
<content select=".sex"></content>
</dd>
<dt>年龄</dt>
<dd>
<content select=".age">12</content>
</dd>
<dt>爱好</dt>
<dd>
<content select=".hobby"></content>
</dd>
</dl>
<!--<p><content></content></p>-->
<p><content select=""></content></p>
</template>
<script type="text/javascript">
var box = document.querySelector('.box');
var root = box.createShadowRoot();
var boxTemplate = document.querySelector('.box-template');
root.appendChild(boxTemplate.content);
</script>
</body>
这种做法可以将内容和实现分离,让我们能更加灵活的处理页面的呈现。
要想引用影子宿主里的内容,我们首先需要使用一个新的标签— <content>标签,创建一个插入点(insertion point),将影子宿主的内容展示出来。
插入点允许我们在不改变源代码的情况下改变渲染顺序,这也意味着我们可以对要呈现的内容进行选择。
使用<template>标签令shadow DOM的使用过程更加简单。
<template>标签相关资料可查看:
Select属性
上文例子中我们创建了一个非常简单的简历组件。因为每个定义的字段都需要特定的内容,我们必须告诉<content>标签有选择性的插入内容。为了做到这一点,我们使用select属性。select属性使用CSS选择器来选取想要展示的内容。
举例来说,<content select=".name">会在影子宿主里寻找任何样式名称为.name的元素。如果找到一个匹配的元素,就会将这个元素渲染到shadowDOM中对应的<content>标签中去。
贪心插入点(Greedy Insection Points)
在例子中,在.box-template模板的最后,我们有一个<content>标签。他的select属性值为空。
<p><content select=""></content></p>
这种被称为通配符选择器(wildcard selection),其可以抓取影子宿主中剩余的全部内容。以下三种选择器是完全相等的。
<content></content>
<content select=""></content>
<content select="*"></content>
Shadow Boundary
影子边界(shadow boundary)表示分离常规 DOM (与影子 DOM 相对立的“光明” DOM)与 shadow DOM 的壁障。影子边界的主要好处就是防止主 DOM 中的样式泄露到 shadow DOM 中。这就意味着即使你在主文档中有一个针对全部
<h3>标签的样式选择器,这个样式也不会不经你的允许便影响到 shadow DOM 的元素。
<body>
<style>
button {
font-size: 18px;
font-family: '华文行楷';
}
</style>
<button>
我是一个普通的按钮
</button>
<div></div>
<script type="text/javascript">
var host = document.querySelector('div');
var root = host.createShadowRoot();
root.innerHTML = '<style>button { font-size: 24px; color: blue; } </style>' +
'<button>我是一个影子按钮</button>';
</script>
</body>
由于影子边界的存在,第二个按钮忽略掉按钮的的样式并使用自己的样式。并且原文档中的按钮还保持了默认的显示样式。
这种作用域化(scoping)的特性实在是非常的“额妹子嘤”。我们折腾了这么些年样式表,它的选择范围似乎越来越大。你越来越难以向一个项目中添加新的样式,因为你担心不小心把页面中的那一块搞崩掉。Shadow DOM 提供给我们的样式边界意味着我们终于可以开始用一种更加局部的、特定组件化的方式来考虑和编写我们的 CSS。
宿主样式 (:host)
给影子宿主设置样式,可以使用:host
:host的选择器优先级被设定为低于页面选择器的优先级,所以如果有需要的话它可以轻松被页面重写样式
宿主样式中的类型选择器
由于:host是伪类选择器(Pseudo Selector),我们可以将其应用于多个标签上来改变组件的外观。
:host(p){
color: blue
}
:host(div){
color: green
}
可以利用:host选择器来改变组件的某一个特定标签样式,还可以根据类名、ID、属性等来进行匹配选择–任何有效的CSS选择器都可以正常工作。
使用*选择器,可以创造应用于全部:host元素的默认样式。
主题化
使用:host-context()可以基于内容元素修改组件的外观。实现.parent<.child类似父类选择器的效果,当然这仅限于使用shadow DOM。
<div class="serious">
<p class="serious-widget">大家好我很严肃</p>
</div>
<div class="playful">
<p class="playful-widget">漂亮的小云彩效果</p>
</div>
<template class="shadow-template">
<style type="text/css">
:host-context(.serious) {
width: 250px;
height: 50px;
padding: 50px;
font-weight: bold;
font-size: 24px;
color: black;
background: tomato;
}
:host-context(.playful) {
width: 250px;
height: 50px;
padding: 50px;
font-weight: bold;
font-size: 24px;
color: #FFF;
background: deepskyblue;
}
::content {
color: firebrick;
}
</style>
<content select=""></content>
</template>
<script type="text/javascript">
var root1 = document.querySelector('.serious-widget').createShadowRoot();
var root2 = document.querySelector('.playful-widget').createShadowRoot();
var template = document.querySelector('.shadow-template');
root1.appendChild(document.importNode(template.content, true));
root2.appendChild(document.importNode(template.content, true));
</script>
宿主样式状态
:host标签最好用的地方之一是设置状态的样式,如:hover和:active。
template模板标签的特性:
- 惰性:在使用前不会被渲染;
- 无副作用:在使用前,模板内的各种脚本不会运行、图像不会加载等;
- 内容不可见:模板的内容不存在于文档中,使用选择器无法获取;
- 可被放置于任意位置:即使是HTML解析器不允许出现的位置,例如作为
<select>的子元素。
:host-context的作用:这个选择器的实际用法是用来选择影子宿主的祖先元素,最大的用处大概就是主体的设置。
分布节点
使用shadow DOM时应确保表现与内容分离。来自页面并通过<content>标签添加到shadow DOM的内容被称为分布节点。
分布节点的样式渲染需要用到::content伪类选择器。可以让影子模板中的按钮与出现在<content>标签中的按钮拥有不同的样式。
::content> p {
font-size: 20px;
color: hotpink;
}
::shadow
使用::shadow伪类选择器可以赋予我们重写默认定义的自由,可以打破影子边界的壁垒。
/deep/
使用::shadow选择器的缺陷是他只能穿透一层影子边界。如果在一个影子树中嵌套了多个影子树,那么使用/deep/组合符更为简便。
JavaScript
事件重定向(Event Retargeting)
Shadow DOM里的js与传统js一个真正不同的点在于事件调度(event dispatching)。原来绑定在shadow DOM节点中的事件被重定向了,所以他们看起来像绑定在影子宿主上一样。
影子节点上的事件必须重定向,否则这将破坏封装性。
分布式节点
分布节点来自原有 DOM 结构,而用户是可以操作原有 DOM 结构的。对它进行事件重定向没啥必要,而且事实上你也不想让它重定向。如果一个使用者给你提供一个按钮来对 Shadow DOM 设置样式,有时候他们可能会希望监听它的 click 事件。
被阻塞的事件(Blocked Events)
有些情况下事件绑定不进行重定向而直接被干掉。以下事件会被阻塞到根节点且不会被原有DOM结构监听到:
- abort
- error
- select
- change
- load
- reset
- resize
- scroll
- selectstart
webcomponent.js在页面组件层面主要实现了三个内容:自定义元素、HTML引入以及Shadow DOM。
本文深入探讨了ShadowDOM的概念及其在Web开发中的应用。介绍了如何通过ShadowDOM实现内容与表现的分离,增强样式的局部化,并提供了丰富的示例代码。此外,还讲解了宿主样式、主题化、分布节点等相关概念。
323

被折叠的 条评论
为什么被折叠?



