在当今充满组件驱动架构的世界中,全局应用 CSS 可能会在我们的其余代码中产生问题。这是因为 CSS 的创建方式和方式。这不一定是坏事,但知道我们可以通过组件架构世界中的封装思想更有效地使用样式是件好事。这有助于我们避免不必要的副作用,同时保持我们的组件代码干净、最小、易于扩展和重建。
Web 组件的一个重要特性是封装——将行为、样式和标记结构与页面上的其他代码隐藏和区分的能力,这样各个部分不会重叠并保持代码整洁。它的核心组件是 Shadow DOM API,它提供了一种向元素添加单独隐藏 DOM 的方法。本文介绍了使用 Shadow DOM 和 Shadow root 概念的基础知识。
在介绍Shadow DOM之前,先来了解一些概念:
什么是DOM?
网页的基础是 HTML,一种供人们书写和理解的标记语言。 HTML 为我们提供了一种添加内容和结构的方法,但机器需要的还不止这些。出于这个原因,正在创建文档对象模型(又名 DOM)。然后 DOM 有一个 API,我们的应用程序可以使用它来操作文档的结构、内容和样式。
什么是DOM树?
当浏览器加载网页时,HTML 代码被转换为由“对象”和“节点”组成的数据模型。此外,还会生成一个“DOM 树”,其中存储了网页结构。
DOM 树如下所示:
Web Components 是一组各种技术,使您能够构建可重用的自定义元素。它们的功能被封装在程序的其余部分之外,并且可以包含在 Web 应用程序中。
有 4 个 Web 组件标准,我们将重点介绍 Shadow DOM:
- 影子DOM
- HTML 模板
- 自定义元素
- HTML 导入
Shadow DOM 消除了创建 Web 应用程序的脆弱性。脆弱性来自 HTML、CSS 和 JavaScript 的全局性。多年来,我们发明了大量工具来解决这些问题。例如,当您添加新的 HTML id / class 时,它不会说明它是否与网站上使用的当前名称冲突。微妙的小故障正在蔓延,CSS 特异性正在成为一个大问题! (所有事情都很重要!),样式选择器正在失去平衡,输出可能会受到影响。 Shadow DOM 修复了 HTML 和 DOM。这会向 Web 应用程序添加范围样式。
注意:请始终记住,并非所有浏览器都应用此概念。
Shadow DOM 用于封装。 Shadow DOM 允许组件的作者构建自己的 DOM,这与常规 DOM 是分开的。这确保了针对这个新 DOM 编写的所有 JavaScript 和 CSS 都可以被完全封装,并且其结果不受全局字段中声明的 CSS 的影响,除非组件允许这样做。
构建 Shadow DOM:DOM的结构通常对我们隐藏,但我们可以在开发人员工具中看到它。例如,在 Chrome 中,我们需要在开发工具中允许“显示用户代理影子 DOM”选项。
What you can see below #shadow-root is called “shadow DOM”.
我们无法通过常规 JavaScript 调用或选择器获得内置的 shadow DOM 元素。这些不是普通的孩子,而是一种强大的封装技术。
影子根:影子树是一个节点树,其根称为影子根。影子根总是通过其主机附加到另一个节点树。因此,影子树从不孤单。影子根宿主的节点树有时也被称为光树。
Shadow DOM 允许将隐藏的 DOM 树附加到常规 DOM 树的元素上。
在这里,这个 shadow DOM 树从下面的 shadow root 开始,可以以与普通 DOM 相同的方式附加到您选择的任何元素。
#shadow-root
影子树的顶端是影子根。树附加到的元素 (
您需要了解一些 shadow DOM 术语:
- Shadow 主机:您选择启动新 Shadow DOM 的元素。
- 影子树:影子DOM内部的节点树,即DOM树。
- 影子边界:这是影子 DOM 结束的地方,是常规 DOM 开始的地方。
- 影子根:影子树的根节点。
创建 shadow DOM:一旦您选择了要用于托管 Shadow DOM 的元素,就必须将 Shadow DOM 附加到该元素上。请看下面的片段。
Welcome to GeeksForGeeks
所以在这个脚本中,我们获取一个 id 为 Firstcomponent 的 div 元素,然后我们在这个元素上调用一个叫做 attachShadow() 的特殊方法,这个方法传入一个具有属性模式的对象。
有两个限制:
- 每个元素只能创建一个影子根。
- 元素必须是自定义元素,或以下元素之一:“blockquote”、“body”、“div”、“article”、“aside”、“footer”、“h1…h6”、“header”、“main” “nav”、“p”、“section”或“span”和其他元素,如 ,不能承载影子树。
mode 选项用于设置封装级别。它必须遵循以下两个值中的任何一个:
- “open” – 打开意味着您可以使用 JavaScript 访问 shadow DOM。
For example using the Element.shadowRoot property:
let myShadowDom = myCustomElem.shadowRoot;
- “关闭” – myCustomElem.shadowRoot 返回 null。
您将无法从外部访问 shadow DOM,如果您将 shadow root 附加到具有mode: closed set 的自定义元素。
我们只能通过 attachShadow 返回的引用访问 shadow DOM,它可能隐藏在一个类中。浏览器原生阴影树已关闭。没有办法访问它们。影子根,由 attachShadow() 方法返回。该对象代表您新创建的 Shadow DOM 的根节点,您将向其附加其他元素。
让我们看看下面给出的示例,该示例显示了针对由组件托管的 Shadow Root 添加段落元素。
const paragraphElement = document.createElement('p');
paragraphElement.setAttribute('class', 'highlight');
paragraphElement.innerText =
'This is a Shadow DOM paragraph';
shadowRootOne.appendChild(paragraphElement);
上面的代码将一个带有高亮类的新段落元素附加到 Shadow Root,它成为新 Shadow DOM 的一部分。此段落元素与主 DOM 分开封装,因此无法通过执行 JavaScript 和 CSS 选择来识别它。它也不会从 CSS 作者页面继承任何样式,但在此阶段它将具有默认浏览器样式,因为我们尚未为此 Shadow DOM 指定样式。
向 Shadow DOM 添加样式: Shadow Tree 中的样式范围限定于 Shadow Tree,并且不会影响 Shadow Tree 之外的元素。通常,影子树外部的样式与影子树内部的选择器不匹配。
有两种方法可以为 shadow DOM 添加样式。将样式包含到 Shadow DOM 中的第一种方法与向 Shadow DOM 添加任何其他元素相同。通过对 Shadow Root 进行简单的 appendChild 调用。另一种方法是使用 元素添加样式,但这是另一个主题的一部分,因此我们将在本文中只看一个。
const styleElement = document.createElement('style');
styleElement.innerText = `
.highlight {
background: #FFA500;
font-size: 6rem;
} `;
shadowRootOne.appendChild(styleElement);
该段落被封装在一个单独的 Shadow DOM 中,因此如果您将全局样式更改为具有更高的特异性,它不会更改新组件的样式。