几周前,我写了一篇关于到底是什么DOM的文章。回顾一下,文档对象模型是HTML文档的表示。浏览器使用它来肯定页面上要呈现的内容,并经过Javascript程序来修改页面的内容,结构或样式。php
例如,让咱们采用如下HTML文档: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树。web
html浏览器
headapp
titledom
bodysvg
h1工具
p组件化
在过去几年中,您可能据说过“Shadow DOM”和“Virtual DOM”等术语。这些虽然固然与原始DOM有关,但它们指的是大相径庭的概念。在本文中,我将详细介绍Shadow DOM以及它与原始DOM的区别。在之后的文章中,我将对虚拟DOM进行分析。url
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>这样的工具,而这些工具实际上并非为此目的而制做的。
您能够将shadow DOM视为“DOM中的DOM”。它是本身独立的DOM树,具备本身的元素和样式,与原始DOM彻底隔离。
虽然最近才指定供Web做者使用,但用户代理多年来一直使用shadow DOM来建立和设置复杂组件(如表单元素)。咱们来看一下input。要在页面上建立一个,咱们所要作的就是添加如下元素:
<input type="range">
若是咱们深刻挖掘,咱们会看到这个<input>元素其实是由几个较小的<div>元素组成,控制着轨道和滑块自己。
这是使用shadow DOM实现的。暴露给主机HTML的元素记录了简单的<input>,但在其下面有与组件相关的元素和样式,它们不构成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就不存在了。