什么是Shadow DOM?

什么是Shadow DOM?

几周前,我写了一篇关于到底是什么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

        • My first web page
    • bodysvg

      • h1工具

        • Hello, world!
      • p组件化

        • How are you?

在过去几年中,您可能据说过“Shadow DOM”和“Virtual DOM”等术语。这些虽然固然与原始DOM有关,但它们指的是大相径庭的概念。在本文中,我将详细介绍Shadow DOM以及它与原始DOM的区别。在之后的文章中,我将对虚拟DOM进行分析。url

一切都是 global 的👍🏾!等等,一切都是 global 的👎🏾

HTML文档中的全部元素和样式以及DOM都位于一个全局范围内。页面上的任何元素均可以经过document.querySelector()方法访问,不管它在文档中的嵌套程度如何或放置在何处。一样,应用于文档的CSS能够选择任何元素,不管它在何处。

当咱们想要将样式应用于整个文档时,能够直接使用*选择页面上的每一个元素并将它们的盒模型进行修改。box-sizing

* { box-sizing: border-box }

另外一方面,有些时候元素须要彻底封装,咱们不但愿它受到全局样式的影响。一个很好的例子是第三方小部件,例如Twitter的“关注”按钮。如下是该小部件的示例:

Follow @ireaderinokun
clipboard.png

Twitter follow @ireaderinokun

假设您启用了Javascript而且检查了元素,您会注意到该按钮是一个<iframe>元素,您实际看到的样式按钮实际上是加载一个小文档。

clipboard.png

这是Twitter能够确保其小部件的预期样式不受文档中的任何CSS影响的惟一方式。尽管有一些方法可使用级联来尝试实现相同的结果,可是没有其余方法能够提供与<iframe>相同的效果。

建立Shadow DOM是为了容许在Web平台上本地封装和组件化,而没必要依赖像<iframe>这样的工具,而这些工具实际上并非为此目的而制做的。

DOM中的DOM

您能够将shadow DOM视为“DOM中的DOM”。它是本身独立的DOM树,具备本身的元素和样式,与原始DOM彻底隔离。

虽然最近才指定供Web做者使用,但用户代理多年来一直使用shadow DOM来建立和设置复杂组件(如表单元素)。咱们来看一下input。要在页面上建立一个,咱们所要作的就是添加如下元素:

<input type="range">

若是咱们深刻挖掘,咱们会看到这个<input>元素其实是由几个较小的<div>元素组成,控制着轨道和滑块自己。

clipboard.png

这是使用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。

clipboard.png

虽然常规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...
clipboard.png

最后,咱们能够经过建立一个<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);

这是咱们的最后一个要素:

clipboard.png

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

相关文章
相关标签/搜索