需要帮助了解 Shadow DOM

Posted

技术标签:

【中文标题】需要帮助了解 Shadow DOM【英文标题】:Need help understanding the Shadow DOM 【发布时间】:2016-08-07 23:43:30 【问题描述】:

阅读有关 Shadow DOM 的文章和教程时,我遇到了一个让我有点困惑的描述:

“Shadow DOM 是指浏览器能够将 DOM 元素的子树包含在文档的呈现中,但不能包含在主文档 DOM 树中。”

所以影子树不是 DOM 树的一部分?但是浏览器仍然会看到它并呈现它的内容吗?

【问题讨论】:

这个想法是浏览器正在呈现的树隐藏在自定义元素后面。一个很好的例子是视频标签,许多控件可以用 div 和按钮来实现,但作为用户,你无权访问这些内部结构。内部表示不可用于自定义组件外部的代码,除非您通过影子 DOM 或自定义方法公开它。见html5rocks.com/en/tutorials/webcomponents/shadowdom 是的,影子 DOM 并不是真正的树的一部分,从调用者的代码角度来看,它隐藏在实现背后,这就是重点 【参考方案1】:

这句话似乎来自这篇标题为:What the Heck is Shadow DOM?

影子 DOM DOM 的一部分(但虚拟 DOM 是 DOM 的隐藏副本。抱歉之前与虚拟 DOM 混淆了!)。从再次查看W3 Spec 来看,shadow DOM 似乎只是一个可重用的 DOM 片段。浏览器会看到它并呈现它的内容。

本规范描述了一种将多个 DOM 树组合成一个层次结构的方法,以及这些树如何在文档中相互交互,从而实现更好的 DOM 组合。

这项技术至少从 2006 年就已经存在,当时我开始在 javascript 中使用 .innerHTML 和模板来构建可重用的 DOM 片段。这不是新技术。它只是在 2015 年被 W3C 记录为官方规范。

有趣的是这些 CSS 属性和伪选择器,它们在 Shadow DOM 上运行,但不是 Real DOM 的一部分。它们在W3 Spec 的Composed Trees section 底部进行了描述。

::shadow 伪元素

/deep/ 组合器,被替换为 >>> 组合器(或阴影穿透后代组合器)

::content 伪元素

:host 伪类和 :host() 函数伪类

:host-context() 函数伪类

它们会添加到这些选择器中,人们有时会使用这些选择器来创建带有插入符号/指向其他屏幕元素的指针的 <div> 标签:

::before & ::after

其他更新:

我在Shadow DOM 101 链接中找到了更多详细信息。查看“您好,我的名字是 Bob...Shellie”示例时(大约在页面下方 1/2 处),它位于此文本块的正上方...

现在我们已经实现了内容和呈现的分离。内容在文档中;演示文稿在 Shadow DOM 中。当需要渲染某些内容时,它们会由浏览器自动保持同步。

...我们可以检查 DOM 并查看 shadow DOM 的样子。看起来像这样,其中 CSS 和 HTML 都可以封装在“shadow DOM”元素中,该元素隐藏在 <div> 标记内。见:https://developer.chrome.com/devtools/docs/settings-files/show-shadow-dom.png

似乎这个想法是封装 CSS 和 HTML,这样它就不会溢出到页面的其他区域。也不允许其他现有/页面代码影响该封装代码块内部的内容。这种封装的旧示例将隐藏 <iframe> 标签,该标签旨在展示广告,但阻止第 3 方广告代码破坏我们非常酷的网页上的 JS。

这里还有一些 Shadow DOM 链接:

    Shadow DOM 101 Shadow DOM 201 Shadow DOM 301 Visualizing Shadow DOM Concepts

【讨论】:

天哪,我现在更困惑了。 shadow dom 和 virtual dom 不一样吧?我指的是作为 Web 组件一部分的 shadow dom:w3.org/TR/2015/WD-shadow-dom-20151215 shadow dom 是虚拟 dom。只有 2 个:“可见” DOM 和“隐藏” DOM。浏览器只渲染可见的 DOM(我们通常看到的),而不是隐藏的 DOM。这些术语是可互换的,可能会造成混淆。真实=可见=可见。影子=虚拟=隐藏。这有帮助吗?还是我需要编辑我的答案以使其更清楚? 我不知道 ReactJS,但它的“虚拟 DOM”似乎与影子 DOM 完全不同。 我想你会更加迷惑人,shadow DOM 和 react 不是一回事。有相似之处,但你没有清楚地呈现出来 我的印象是一样的,就是虚拟DOM和影子DOM又不一样。我熟悉 Reactjs 和虚拟 DOM。但我认为影子 DOM 与虚拟 DOM 是分开的。 shadow DOM 是出于封装目的的东西。我对这一切都很陌生,所以很有可能我错了。【参考方案2】:

来自Shadow DOM 规范,

文档树是根节点为文档的节点树。

任何元素都可以承载零个或一个关联的节点树,称为 影子树

影子主机是一个承载一棵影子树的元素。

影子根是影子树的根节点。

树的树是节点树的树。

那么,是的,影子树在文档树之外,但它们仍然链接在一起形成一棵树。

是的,阴影内容被渲染而不是元素的后代,如CSS Scoping中所定义:

元素上最近创建的阴影树是活动的 该元素的阴影树

影子主机的后代不能在 格式化树。相反,活动影子树的内容 生成框,就好像它们是元素的内容一样。

【讨论】:

【参考方案3】:

我认为理解 shadow DOM 最简单的方法是通过示例:

<div>
  <input type="range">
</div>

上述代码的 DOM 看起来与您可能期望的完全一样:

div
- input[type=range]

但是您的浏览器呈现的是另一回事:有一条水平线和一个拇指(或旋钮或任何您称之为的东西)。所以在内部,input 有一些子元素,但它们没有通过 DOM 暴露:

div
- input[range]
  - bar
  - thumb

但正如我已经写过的:那些不是通过 DOM 公开的,所以它们对你、你的 CSS、你的 JS 是隐藏的(这并不完全正确,浏览器可以给你一些访问权限,例如基于 Webkit 的浏览器允许您可以通过 -webkit-slider-thumb 伪元素在 CSS 中操纵拇指的外观)。

另一方面,这些元素需要在 DOM 中的某个地方才能被浏览器渲染,这就是影子 DOM 的用武之地:在内部,浏览器用树替换 DOM 中每个出现的input[type=range]

input[range]
- bar
- thumb

那就是影子 DOM:某些元素是某些元素的子元素,不是因为您将它们放在 HTML 中,而是因为父元素被定义为具有这些子元素(就像音频元素被定义为具有播放按钮)并且不通过 DOM 暴露,而是由浏览器内部生成。

更多示例和更详尽的解释可以在这里找到:What the Heck is Shadow DOM?

【讨论】:

同意,Dimitri Glazkov 的文章仍然是 Shadow DOM 的出色解释者

以上是关于需要帮助了解 Shadow DOM的主要内容,如果未能解决你的问题,请参考以下文章

Shadow DOM

每日思考(2020/01/03)

聚合:将样式应用于Shadow DOM下的元素

shadow-dom浅析

shadow-dom 浅析

神秘的 shadow-dom 浅析