什么是Shadow DOM?
几周前,我写了一篇关于究竟是什么DOM的文章。回顾一下,文档对象模型是HTML文档的表示。浏览器使用它来确定页面上要呈现的内容,并通过Javascript程序来修改页面的内容,结构或样式。
例如,让我们采用以下HTML文档:
<!doctype html>
<html lang="en">
<head>
<title>My first web page</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>How are you?</p>
</body>
</html>
上面的HTML文档将产生以下DOM树。
-
html
-
head
-
title
- My first web page
-
-
body
-
h1
- Hello, world!
-
p
- How are you?
-
-
在过去几年中,您可能听说过“Shadow DOM”和“Virtual DOM”等术语。这些虽然当然与原始DOM有关,但它们指的是截然不同的概念。在本文中,我将详细介绍Shadow DOM以及它与原始DOM的区别。在以后的文章中,我将对虚拟DOM进行分析。
一切都是 global 的👍🏾!等等,一切都是 global 的👎🏾
HTML文档中的所有元素和样式以及DOM都位于一个全局范围内。页面上的任何元素都可以通过document.querySelector()
方法访问,无论它在文档中的嵌套程度如何或放置在何处。同样,应用于文档的CSS可以选择任何元素,无论它在何处。
当我们想要将样式应用于整个文档时,可以直接使用*
选择页面上的每个元素并将它们的盒模型进行修改。box-sizing
* { box-sizing: border-box }
另一方面,有些时候元素需要完全封装,我们不希望它受到全局样式的影响。一个很好的例子是第三方小部件,例如Twitter的“关注”按钮。以下是该小部件的示例:
Follow @ireaderinokun
Twitter follow @ireaderinokun
假设您启用了Javascript并且检查了元素,您会注意到该按钮是一个<iframe>元素,您实际看到的样式按钮其实是加载一个小文档。
这是Twitter可以确保其小部件的预期样式不受文档中的任何CSS影响的唯一方式。尽管有一些方法可以使用级联来尝试实现相同的结果,但是没有其他方法可以提供与<iframe>相同的效果。
创建Shadow DOM是为了允许在Web平台上本地封装和组件化,而不必依赖像<iframe>这样的工具,而这些工具实际上并不是为此目的而制作的。
DOM中的DOM
您可以将shadow DOM视为“DOM中的DOM”。它是自己独立的DOM树,具有自己的元素和样式,与原始DOM完全隔离。
虽然最近才指定供Web作者使用,但用户代理多年来一直使用shadow DOM来创建和设置复杂组件(如表单元素)。我们来看一下input。要在页面上创建一个,我们所要做的就是添加以下元素:
<input type="range">
如果我们深入挖掘,我们会看到这个<input>元素实际上是由几个较小的<div>元素组成,控制着轨道和滑块本身。
这是使用shadow DOM实现的。暴露给主机HTML的元素记录了简单的<input>,但在其下面有与组件相关的元素和样式,它们不构成DOM全局范围的一部分。
shadow DOM如何工作
为了说明shadow DOM的工作原理,让我们使用shadow DOM而不是<iframe>来重新创建Twitter“follow”按钮。
首先,我们从shadow host开始。这是我们想要将新影子DOM附加到原始DOM中的常规HTML元素。对于像Follow按钮这样的组件,它还可以包含我们希望在页面上未启用Javascript或不支持shadow DOM时显示的回退元素。
<span class="shadow-host">
<a href="https://twitter.com/ireaderinokun">
Follow @ireaderinokun
</a>
</span>
请注意,我们不仅仅使用<a>
元素作为影子主机,因为某些元素(主要是交互元素)不能是影子主机。
要将阴影DOM附加到我们的主机,我们使用attachShadow()方法。
const shadowEl = document.querySelector(".shadow-host");
const shadow = shadowEl.attachShadow({mode: 'open'});
这将创建一个空的shadow root作为我们的shadow host的子项。shadow root 是新的shadow DOM的开始,其方式是<html>
元素是原始DOM的开头。我们可以通过#shadow-root 在devtools检查器中看到我们的shadow-root。
虽然常规HTML子项在检查器中是可见的,但是当shadow root接管时,它们在页面上不再可见。
接下来,我们要创建内容以形成新的shadow tree。shadow tree就像一个DOM树,但是对于shadow DOM而不是常规DOM。要创建我们的跟随按钮,我们所需要的只是一个新的<a>
元素,它与我们已有的后备链接几乎完全相同,但带有一个图标。
const link = document.createElement("a");
link.href = shadowEl.querySelector("a").href;
link.innerHTML =
<span aria-label="Twitter icon"></span>
${shadowEl.querySelector("a").textContent}
;
我们将这个新元素添加到我们的shadow DOM中,就像使用appendChild()
方法将任何元素作为子元素添加到另一个元素一样。
shadow.appendChild(link);
在这一点上,这是我们的元素的样子:
https://p0.ssl.qhimg.com/t012...
最后,我们可以通过创建一个<style>元素并将其附加到阴影根来添加一些样式。
const styles = document.createElement("style");
styles.textContent =
a, span {
vertical-align: top;
display: inline-block;
box-sizing: border-box;
}
a {
height: 20px;
padding: 1px 8px 1px 6px;
background-color: #1b95e0;
color: #fff;
border-radius: 3px;
font-weight: 500;
font-size: 11px;
font-family:'Helvetica Neue', Arial, sans-serif;
line-height: 18px;
text-decoration: none;
}
a:hover { background-color: #0c7abf; }
span {
position: relative;
top: 2px;
width: 14px;
height: 14px;
margin-right: 3px;
background: transparent 0 0 no-repeat;
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
}
;
shadow.appendChild(styles);
这是我们的最后一个要素:
DOM与shadow DOM
在某些方面,shadow DOM是DOM的“精简”版本。与DOM一样,它是HTML元素的表示,用于确定在页面上呈现的内容并启用元素的修改。但与DOM不同,shadow DOM不是基于完整的独立文档。正如其名称所示,shadow DOM始终附加到常规DOM中的元素。没有DOM,shadow DOM就不存在了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。