什么是Shadow DOM?

39次阅读

共计 4353 个字符,预计需要花费 11 分钟才能阅读完成。

什么是 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 就不存在了。
Share the Article on Twitter
Share the Article on Facebook
Post the Article to Reddit

正文完
 0